Node.js парсер отзывов: как собирать данные с 20 площадок

📅 Опубликовано: 🔄 Обновлено:
Node.jsпарсинг отзывовскрипты автоматизацииETL пайплайнвеб-скрейпинг
Node.js парсер отзывов: как собирать данные с 20 площадок

Коротко: Node.js парсер отзывов может собирать до 50 000 отзывов в час с 20+ площадок одновременно. Связка Puppeteer + Cheerio + Proxy-rotation решает 90% задач по сбору отзывов, а правильная архитектура ETL-пайплайна обеспечивает стабильную работу 24/7 с нагрузкой до 100 запросов в секунду.

Содержание

Почему именно Node.js для парсинга отзывов?

Когда мы в DS495 выбирали стек для парсера отзывов, рассматривали Python с Scrapy, но в итоге остановились на Node.js. Причина простая — асинхронность из коробки.

Парсинг отзывов — это массово параллельная задача. Нужно одновременно обрабатывать сотни страниц, ждать загрузки контента, работать с прокси. Node.js справляется с этим идеально благодаря event loop.

Параметр Node.js Python Преимущество
Параллельные запросы До 1000 без блокировки Ограничено GIL Node.js
Потребление памяти ~50MB на 100 воркеров ~200MB на 100 воркеров Node.js
Время старта 500ms 2-3 секунды Node.js
Экосистема для веба Puppeteer, Cheerio Scrapy, BeautifulSoup Примерно равно

В реальных проектах мы получаем скорость парсинга 2000-5000 страниц в минуту на одном сервере. Python с asyncio тоже может так, но Node.js проще в настройке и отладке.

Ключевые библиотеки для автоматизации:

  • Puppeteer — для JavaScript-heavy сайтов (Wildberries, OZON)
  • Cheerio — для статического контента (старые версии Яндекс.Маркета)
  • Axios — HTTP-клиент с поддержкой прокси
  • Playwright — альтернатива Puppeteer с лучшей производительностью

Как спроектировать архитектуру парсера

Парсер отзывов — это не просто скрипт, который ходит по страницам. Это полноценная система сбора данных с очередями, кэшированием и обработкой ошибок.

Мы строим архитектуру по принципу микросервисов:

  1. URL Generator — генерирует ссылки для парсинга
  2. Fetcher Pool — пул воркеров для загрузки страниц
  3. Parser Engine — извлекает данные из HTML
  4. Data Validator — проверяет качество данных
  5. Storage Writer — сохраняет в БД или файлы
В одном проекте мы парсили отзывы для сети магазинов электроники. За месяц собрали 2.3 млн отзывов с 15 площадок. Система работала 24/7 без сбоев благодаря правильной архитектуре.

Основные компоненты ETL-пайплайна:

// Базовая структура парсера
class ReviewParser {
  constructor(config) {
    this.platforms = config.platforms;
    this.workers = config.workers || 10;
    this.retries = config.retries || 3;
    this.delay = config.delay || 1000;
  }

  async start() {
    const queue = new TaskQueue();
    const workers = Array(this.workers).fill().map(() => 
      new Worker(queue, this.parseReviews.bind(this))
    );
    
    await Promise.all(workers.map(w => w.start()));
  }
}

Для мониторинга используем Redis как очередь задач и хранилище метрик. MongoDB или PostgreSQL для финальных данных — зависит от объёмов и структуры.

Какие площадки парсить и как обходить защиту

20 площадок — это серьёзная нагрузка. У каждой свои особенности защиты от ботов. Мы разделили их на три группы по сложности парсинга:

Сложность Площадки Защита Решение
Низкая Avito, Яндекс.Маркет (старая версия) Rate limiting Прокси + задержки
Средняя Wildberries, OZON, AliExpress JavaScript + капча Puppeteer + rotating proxies
Высокая Amazon, Google Reviews Cloudflare + bot detection Residential proxy + fingerprint spoofing
Нужна помощь с этой задачей? Команда DS495 решит её под ключ. Обсудить проект →

Основные методы обхода защиты:

  • Rotation прокси — меняем IP каждые 10-50 запросов
  • User-Agent spoofing — имитируем разные браузеры
  • Captcha solving — интеграция с 2captcha или AntiCaptcha
  • Headless detection — настройка Puppeteer под реальный браузер
  • Cookie management — сохранение сессий между запросами

Особенности популярных площадок:

Wildberries: Динамическая подгрузка отзывов через API. Нужно перехватывать XHR-запросы и парсить JSON.

OZON: Жёсткий rate limiting — не больше 1 запроса в 2 секунды с одного IP. Используем пул из 50+ прокси.

AliExpress: Cloudflare защита + региональные ограничения. Помогают только quality residential proxy.

Яндекс.Маркет: Обновили защиту в 2024. Теперь обязателен Puppeteer с правильными fingerprints.

Пошаговая реализация базового парсера

Покажу, как создать рабочий парсер отзывов с нуля. Начнём с простого примера для одной площадки, потом масштабируем.

Шаг 1: Настройка окружения

npm init -y
npm install puppeteer cheerio axios winston redis ioredis
npm install --save-dev nodemon

Шаг 2: Создание базового класса парсера

const puppeteer = require('puppeteer');
const axios = require('axios');
const cheerio = require('cheerio');

class ReviewScraper {
  constructor(options = {}) {
    this.browser = null;
    this.proxies = options.proxies || [];
    this.delay = options.delay || 2000;
    this.retries = options.retries || 3;
  }

  async init() {
    this.browser = await puppeteer.launch({
      headless: true,
      args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--disable-dev-shm-usage'
      ]
    });
  }
}

Шаг 3: Реализация парсинга для конкретной площадки

async parseWildberries(productId) {
  const url = `https://www.wildberries.ru/catalog/${productId}/detail.aspx`;
  const page = await this.browser.newPage();
  
  try {
    await page.goto(url, { waitUntil: 'networkidle0' });
    
    // Ждём загрузки отзывов
    await page.waitForSelector('.review-item', { timeout: 10000 });
    
    const reviews = await page.evaluate(() => {
      const reviewElements = document.querySelectorAll('.review-item');
      return Array.from(reviewElements).map(el => ({
        author: el.querySelector('.review-author')?.textContent?.trim(),
        rating: el.querySelector('.review-rating')?.getAttribute('data-rating'),
        text: el.querySelector('.review-text')?.textContent?.trim(),
        date: el.querySelector('.review-date')?.textContent?.trim()
      }));
    });
    
    return reviews;
  } finally {
    await page.close();
  }
}

Шаг 4: Добавление обработки ошибок и ретраев

async scrapeWithRetry(scrapeFunction, ...args) {
  for (let attempt = 1; attempt <= this.retries; attempt++) {
    try {
      const result = await scrapeFunction.apply(this, args);
      return result;
    } catch (error) {
      console.log(`Attempt ${attempt} failed:`, error.message);
      
      if (attempt === this.retries) {
        throw error;
      }
      
      // Экспоненциальная задержка
      await this.sleep(this.delay * Math.pow(2, attempt - 1));
    }
  }
}

Шаг 5: Интеграция с очередью задач

const Redis = require('ioredis');

class TaskQueue {
  constructor() {
    this.redis = new Redis(process.env.REDIS_URL);
  }

  async addTask(platform, productId) {
    const task = { platform, productId, timestamp: Date.now() };
    await this.redis.lpush('scraping_queue', JSON.stringify(task));
  }

  async getTask() {
    const task = await this.redis.brpop('scraping_queue', 10);
    return task ? JSON.parse(task[1]) : null;
  }
}

Шаг 6: Создание воркера для обработки задач

class Worker {
  constructor(scraper, queue) {
    this.scraper = scraper;
    this.queue = queue;
    this.isRunning = false;
  }

  async start() {
    this.isRunning = true;
    
    while (this.isRunning) {
      try {
        const task = await this.queue.getTask();
        if (!task) continue;

        const reviews = await this.processTask(task);
        await this.saveReviews(reviews, task);
        
        console.log(`Processed ${task.platform}:${task.productId}`);
      } catch (error) {
        console.error('Worker error:', error);
      }
    }
  }
}

Как оптимизировать скорость и надёжность

Когда парсер работает 24/7 и обрабатывает тысячи страниц, каждая миллисекунда важна. Мы оптимизируем по трём направлениям: скорость, надёжность и потребление ресурсов.

Оптимизация скорости:

Основная проблема — медленная загрузка страниц. Puppeteer по умолчанию ждёт все ресурсы, включая картинки и аналитику. Это можно отключить:

await page.setRequestInterception(true);
page.on('request', (req) => {
  const resourceType = req.resourceType();
  if (['image', 'stylesheet', 'font'].includes(resourceType)) {
    req.abort();
  } else {
    req.continue();
  }
});

Результат: скорость парсинга выросла с 300 до 1200 страниц в час.

Connection pooling для HTTP-запросов:

const axios = require('axios');
const Agent = require('agentkeepalive');

const httpAgent = new Agent({
  maxSockets: 100,
  maxFreeSockets: 10,
  timeout: 60000,
  freeSocketTimeout: 30000
});

const client = axios.create({
  httpAgent,
  timeout: 30000
});

Кэширование результатов:

Многие товары имеют одинаковые отзывы на разных страницах. Мы кэшируем уже обработанные отзывы по хэшу от текста:

const crypto = require('crypto');

function getReviewHash(review) {
  return crypto
    .createHash('md5')
    .update(`${review.text}${review.author}`)
    .digest('hex');
}

// Проверка в кэше перед сохранением
if (await redis.exists(`review:${hash}`)) {
  return; // Отзыв уже есть
}

Это сокращает объём сохраняемых данных на 40-60%.

ETL-пайплайн и мониторинг процессов

ETL (Extract, Transform, Load) — основа надёжного парсера. Мы разделили процесс на независимые этапы, каждый со своими метриками и мониторингом.

Extract (Извлечение):

  • Загрузка HTML/JSON с целевых площадок
  • Обработка ошибок сети и таймаутов
  • Ротация прокси и User-Agent'ов
  • Метрика: количество успешных запросов в минуту

Transform (Трансформация):

  • Парсинг HTML и извлечение структурированных данных
  • Нормализация текста (удаление лишних пробелов, emoji)
  • Определение языка и тональности отзывов
  • Метрика: процент корректно распарсенных страниц

Load (Загрузка):

  • Валидация данных перед сохранением
  • Дедупликация по хэшам
  • Сохранение в БД с правильной индексацией
  • Метрика: скорость записи в секунду
  • Prometheus + Grafana — для системных метрик и дашбордов
  • Winston — структурированное логирование
  • PM2 — управление процессами и автоперезапуск
  • Redis — очередь задач и кэш промежуточных результатов

Пример настройки мониторинга:

const winston = require('winston');
const prometheus = require('prom-client');

// Метрики для Prometheus
const scrapingDuration = new prometheus.Histogram({
  name: 'scraping_duration_seconds',
  help: 'Time spent scraping pages',
  labelNames: ['platform', 'success']
});

const reviewsProcessed = new prometheus.Counter({
  name: 'reviews_processed_total',
  help: 'Total number of reviews processed',
  labelNames: ['platform']
});

// Логгер
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'scraper.log' }),
    new winston.transports.Console()
  ]
});

Алерты и уведомления:

Настраиваем уведомления в Telegram при критичных ошибках:

  • Падение скорости парсинга ниже 500 страниц/час
  • Процент ошибок выше 15%
  • Недоступность более 3 площадок одновременно
  • Переполнение очереди задач (>10000 pending)

Парсинг отзывов находится в серой зоне. Формально это публичная информация, но сайты могут ограничивать автоматический доступ через robots.txt и пользовательские соглашения.

Основные принципы безопасного парсинга:

  1. Соблюдение robots.txt — хотя бы формально
  2. Разумные лимиты — не более 1-2 запросов в секунду на площадку
  3. Использование данных только для анализа — не для перепубликации
  4. Обезличивание персональных данных — хэширование имён авторов

Рекомендуемые лимиты по площадкам:

Площадка Запросов в минуту Максимальный объём Ограничения
Wildberries 30 10 000 отзывов/день Блокировка по IP
OZON 20 5 000 отзывов/день Капча после 100 запросов
Яндекс.Маркет 60 50 000 отзывов/день Rate limiting
Amazon 10 1 000 отзывов/день Жёсткая защита
В одном проекте нам нужно было собрать отзывы о конкурентах для сети спортивных магазинов. Мы ограничились 5000 отзывов в день на площадку и растянули сбор на 2 месяца. Зато ни разу не получили блокировку.

Что точно нельзя делать:

  • Парсить персональные данные пользователей (email, телефоны)
  • Перепубликовывать отзывы без согласия авторов
  • Создавать высокую нагрузку на серверы (>100 RPS)
  • Игнорировать официальные API, если они есть

Частые вопросы

В: Сколько времени занимает разработка парсера для 20 площадок?

О: От 2 до 6 месяцев в зависимости от сложности площадок. Базовый парсер для 5-7 простых сайтов можно сделать за месяц, но добавление защищённых площадок (Amazon, Google) может занять ещё 3-4 месяца.

В: Какую производительность можно ожидать от Node.js парсера?

О: На одном сервере (8 CPU, 16GB RAM) мы получаем 2000-5000 страниц в час. С кластером из 5 серверов и правильной балансировкой нагрузки — до 50 000 отзывов в час.

В: Нужно ли покупать дорогие прокси для парсинга?

О: Зависит от площадок. Для российских сайтов (Wildberries, OZON) достаточно дата-центровых прокси за $50-100/месяц. Для Amazon и Google нужны residential прокси от $200/месяц.

В: Как обойти Cloudflare защиту?

О: Используйте Playwright вместо Puppeteer, настройте правильные fingerprints, работайте через residential прокси и добавьте случайные задержки. В 70% случаев это помогает.

В: Можно ли парсить отзывы легально?

О: Отзывы — публичная информация, но парсинг может нарушать пользовательское соглашение сайта. Используйте данные только для анализа, соблюдайте разумные лимиты и не перепубликовывайте контент.

В: Какой объём данных можно собрать за месяц?

О: При соблюдении лимитов — 500 000 - 1 500 000 отзывов с 20 площадок. Больше не рекомендуем из-за риска блокировок.

В: Стоит ли использовать готовые решения вместо самописного парсера?

О: Готовые API (ReviewAPI, ScraperAPI) стоят $500-2000/месяц и покрывают 5-10 популярных площадок. Самописный парсер дешевле при больших объёмах и даёт полный контроль.

Читайте также

Нужна помощь с этим? Обсудить проект с DS495 →