Node.js парсер отзывов: как собирать данные с 20 площадок
Коротко: Node.js парсер отзывов может собирать до 50 000 отзывов в час с 20+ площадок одновременно. Связка Puppeteer + Cheerio + Proxy-rotation решает 90% задач по сбору отзывов, а правильная архитектура ETL-пайплайна обеспечивает стабильную работу 24/7 с нагрузкой до 100 запросов в секунду.
Содержание
- Почему именно Node.js для парсинга отзывов?
- Как спроектировать архитектуру парсера
- Какие площадки парсить и как обходить защиту
- Пошаговая реализация базового парсера
- Как оптимизировать скорость и надёжность
- ETL-пайплайн и мониторинг процессов
- Правовые аспекты и лимиты
- Частые вопросы
Почему именно 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 с лучшей производительностью
Как спроектировать архитектуру парсера
Парсер отзывов — это не просто скрипт, который ходит по страницам. Это полноценная система сбора данных с очередями, кэшированием и обработкой ошибок.
Мы строим архитектуру по принципу микросервисов:
- URL Generator — генерирует ссылки для парсинга
- Fetcher Pool — пул воркеров для загрузки страниц
- Parser Engine — извлекает данные из HTML
- Data Validator — проверяет качество данных
- 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 и пользовательские соглашения.
Основные принципы безопасного парсинга:
- Соблюдение robots.txt — хотя бы формально
- Разумные лимиты — не более 1-2 запросов в секунду на площадку
- Использование данных только для анализа — не для перепубликации
- Обезличивание персональных данных — хэширование имён авторов
Рекомендуемые лимиты по площадкам:
| Площадка | Запросов в минуту | Максимальный объём | Ограничения |
|---|---|---|---|
| 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 популярных площадок. Самописный парсер дешевле при больших объёмах и даёт полный контроль.
Читайте также
- RAG-система для e-commerce: как настроить ИИ-ассистента с GPT-4 и Claude для персонализации товарных рекомендаций и увеличить средний чек на 85% за 60 дней
- Node.js vs Python для бэкенда в 2026 году: что выбрать и как это влияет на стоимость разработки
- React или Next.js: что выбрать для сайта в 2026 году и как это влияет на бюджет
Нужна помощь с этим? Обсудить проект с DS495 →