30 июн. 2025 г.·8 мин чтения

Библиотеки для скрейпинга на Node.js: парсить HTML или использовать браузер

Библиотеки для скрейпинга на Node.js помогают выбрать между быстрым парсингом HTML и headless-браузером в зависимости от типа страницы, затрат, поддержки и качества данных.

Библиотеки для скрейпинга на Node.js: парсить HTML или использовать браузер

Почему команды выбирают не тот инструмент

Многие команды начинают с самого мощного варианта, который им знаком. Они запускают headless-браузер, ждут загрузки страницы, кликают по элементам и надеются, что данные появятся. Это кажется надежным, но часто решает не ту задачу.

Многие сайты до сих пор отдают данные в обычном HTML. Если название товара, цена и статус наличия уже есть в ответе, обычно достаточно простого fetch и парсинга HTML в Node.js. Использовать для этого полноценный браузер — все равно что отправлять грузовик за одним конвертом.

Обратная ошибка тоже встречается часто. Команда видит страницу в браузере, думает, что весь HTML уже содержит нужные данные, и строит быстрый скрейпер на обычных запросах. Потом скрипт возвращает пустые контейнеры, потому что сайт наполняет страницу после выполнения JavaScript. В таком случае легкие инструменты не плохие — просто страница работает как приложение, а не как документ.

Авторизация, защита от ботов, CSRF-токены, session cookies и страницы, которым нужны клики, прежде чем они покажут что-то полезное, снова меняют план. Простой парсер не может нажать кнопку, подождать модальное окно или пройти сценарий, зависящий от состояния браузера. Headless browser scraping справится с этим, но заплатить придется CPU, памятью и более сложной поддержкой.

Неправильный выбор быстро заметен в ежедневной работе. Задания выполняются медленнее. Серверы стоят дороже. Небольшие изменения в верстке ломают хрупкие селекторы. Скрейпер, который работал на десяти страницах, начинает зависать на десяти тысячах.

Поэтому инструмент важно выбрать заранее. Прежде чем выбирать из Node.js scraping libraries, проверьте один скучный, но важный момент: данные приходят в начальном ответе или только после выполнения скриптов? Этот ответ обычно сразу показывает, где выигрывает HTML parsing, а где browser automation in Node.js оправдывает дополнительные затраты.

Что хорошо делают легкие запросы

Среди Node.js scraping libraries самые быстрые результаты часто дает самый простой стек: отправить HTTP-запрос, получить HTML или JSON и вытащить только нужные данные. Если страница отдает полезную информацию в первом ответе, полноценный браузер для этого не нужен.

Встроенный fetch в Node, Axios и Got хорошо подходят для такой задачи. Они быстро отправляют запросы, позволяют задавать заголовки, при необходимости работать с cookies и повторять неудачные вызовы с небольшим количеством дополнительного кода. Для многих задач этого достаточно.

Такой подход особенно хорош, когда сайт уже отдает аккуратную разметку. Страница товара с названием, ценой, наличием и ссылками на изображения в HTML — отличный вариант. Блог, сайт с документацией, страница-список или публичный API подходят еще лучше.

Cheerio упрощает вторую часть. Он загружает разметку и позволяет обращаться к ней через CSS-селекторы, так что можно достать .price, .title или строки таблицы без рендеринга страницы. Если HTML на сайте довольно стабилен, сравнение Cheerio vs Playwright по скорости и памяти обычно не в пользу браузера. Чаще выигрывает Cheerio.

Небольшой сервер может одновременно выполнять много таких запросов. Можно скрейпить сотни или тысячи страниц при скромном потреблении памяти, потому что не нужно запускать браузер, рендерить страницу и выполнять JavaScript. Это особенно важно, если вам нужны быстрые задания на недорогом VPS, а не на более мощной машине.

Легкие запросы лучше всего подходят для таких задач:

  • страницы, где данные есть уже в первом HTML-ответе
  • эндпоинты, которые отдают JSON за кулисами
  • сайты со стабильными селекторами и обычной пагинацией
  • большие партии страниц, где важна экономия памяти

Разница в скорости быстро накапливается. Если один запрос по сети занимает секунду, накладные расходы парсера могут добавить лишь несколько миллисекунд. Браузер же часто тратит намного больше времени только на запуск, загрузку скриптов и ожидание, пока страница «успокоится».

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

Когда выигрывает HTML parsing

HTML parsing — лучший выбор, когда сервер уже отправляет нужные данные в первом ответе. Если названия товаров, цены, статусы наличия, строки таблиц или ссылки на статьи уже есть в сыром HTML, запуск полноценного браузера добавляет время и точки отказа, не давая почти ничего взамен.

Такое часто бывает на каталогах, в индексах блогов, на сайтах с вакансиями и в документации. Вы запрашиваете страницу, парсите разметку и забираете точные поля, которые нужны. Для многих команд этого вполне достаточно. Простой скрипт с Cheerio может прочитать страницу в разы быстрее, чем браузеру нужно, чтобы запуститься, отрисовать все и дождаться скриптов.

HTML parsing в Node.js также хорошо подходит, когда страница не зависит от действий пользователя. Если ничего важного не появляется только после клика, прокрутки, наведения или входа в аккаунт, можно обойтись без browser automation. Тогда скрейпер проще тестировать и намного легче запускать в большом объеме.

Хороший пример — страница со списком товаров. Допустим, интернет-магазин отдает 24 позиции в исходном HTML, и у каждой есть название, цена, число оценок и ссылка на товар. Ни скриншоты, ни клики по кнопкам, ни клиентские фильтры не нужны. Нужно просто прочитать разметку и сохранить поля. В этом случае браузер — лишний груз.

Есть и операционное преимущество. Простые задания fetch-and-parse легче повторять и проще отлаживать. Когда запрос не проходит, в логах обычно видны понятный статус-код, время ответа и сохраненный HTML-снимок. Это помогает быстро понять, что сломалось. Запуски в браузере чаще падают более запутанно — из-за таймингов, отсутствующих селекторов или ошибок JavaScript.

HTML parsing обычно выигрывает, когда вам нужны:

  • текст, который уже есть в ответе
  • списки ссылок или категории страниц
  • цены, метки, бейджи или ячейки таблиц
  • более простые повторы с обычными логами запросов

Среди Node.js scraping libraries это часто самый быстрый путь к рабочему скрейперу. Начинайте с самого легкого инструмента, который может прочитать страницу. Переходите к headless-браузеру только тогда, когда сайт действительно заставляет вас это сделать.

Когда headless-браузер оправдывает затраты

Используйте браузер, когда нужной страницы нет в первом HTML-ответе. Если сервер отправляет только каркас, а JavaScript подгружает настоящий контент через секунду, простой fetch плюс Cheerio пропустят большую его часть. В таком случае headless browser scraping — не избыточность, а единственный способ увидеть то, что видит пользователь.

Это часто случается на страницах товаров, в результатах поиска, на дашбордах и в личных кабинетах. Вы открываете сырой HTML и находите почти ничего, кроме корневого div, пары скриптов и заглушек. Цены, наличие, отзывы или строки таблицы появляются только после того, как страница выполняет код в браузере.

Браузер также нужен, когда скрейпер должен вести себя как человек. Сценарии входа, cookie-баннеры, модальные окна, бесконечная прокрутка, кнопки «показать еще» и загрузка файлов зависят от кликов, ввода текста, ожидания и иногда от обработки переадресаций. Часть этого можно подделать обычными запросами, но когда сценарий усложняется, Playwright или Puppeteer обычно экономят время.

Несколько случаев — особенно явные сигналы:

  • контент появляется только после выполнения JavaScript
  • важные данные скрыты за авторизацией или многошаговой формой
  • модальные окна, вкладки или lazy loading скрывают нужные части
  • нужно прокручивать, нажимать, перетаскивать или загружать файл
  • сначала нужно посмотреть сетевые запросы, чтобы понять, что именно скрейпить

Последний пункт важнее, чем многим кажется. Иногда страница в браузере выглядит шумной, но в network panel браузер показывает чистый JSON-запрос. Как только вы его находите, для ежедневных запусков браузерная автоматизация может уже не понадобиться. Сначала можно использовать браузер, чтобы посмотреть запросы, заголовки и тайминги, а потом перейти на более легкий скрейпер.

В споре Cheerio vs Playwright Playwright часто оказывается лучше, если важна надежность. Он лучше справляется с ожиданиями, фреймами, загрузками и современными сайтами, где многое сделано через JS. Компромисс простой: больше памяти, медленнее выполнение и больше компонентов, которые могут сломаться.

Хорошее правило простое. Если можно получить стабильные данные одним запросом и HTML parsing в Node.js, так и делайте. Если сайт прячет данные за поведением браузера, используйте браузер и двигайтесь дальше. Пытаться два дня избегать headless-браузера обычно дороже, чем просто запустить его.

Пошаговый способ выбрать

Сократите расходы на браузер заранее
Получите быстрый CTO-взгляд на расходы памяти, прокси и поддержку до масштабирования.

Большинство неудачных решений в скрейпинге начинается с догадки. Команда предполагает, что нужен браузер, или думает, что обычного HTML достаточно, а потом тратит дни на исправление не той проблемы.

Лучше действовать скучно, но быстро. Проверьте одну реальную страницу, посмотрите ответ и пусть сама страница подскажет, какой инструмент ей нужен.

Действуйте в таком порядке:

  1. Запросите одну реальную страницу и сохраните сырой HTML ровно в том виде, в котором его получил скрипт. Не открывайте сначала страницу в браузере. Начинайте с ответа сервера.
  2. Найдите в HTML точное поле, которое вам нужно, например название товара, цену, рейтинг или текст о наличии. Если данные уже есть, HTML parsing в Node.js обычно справится.
  3. Откройте DevTools в браузере и перезагрузите страницу. Следите за XHR и GraphQL-запросами. Многие современные сайты загружают реальные данные уже после первого ответа страницы.
  4. Сначала попробуйте Cheerio, если HTML содержит нужные данные. Это дешево, удобно для отладки и позволяет обрабатывать много страниц на скромном железе.
  5. Переходите на Playwright или Puppeteer только если данные вообще не появляются в сыром HTML или если сайт зависит от JavaScript, состояния входа, прокрутки или кликов по кнопкам.

Этот простой шаг экономит массу времени и денег. Если цена уже есть в исходнике страницы, headless-браузер добавляет затраты без пользы. Если страница собирается сама после нескольких API-вызовов, сравнение Cheerio vs Playwright перестает быть вопросом вкуса и становится вопросом фактов на странице.

Перед масштабированием измерьте скучные вещи. Зафиксируйте CPU, потребление памяти и время выполнения на небольшой партии, например на 50 или 100 страницах. Headless browser scraping часто требует заметно больше RAM и выполняется дольше на каждой странице, даже если все работает хорошо. Для нескольких страниц за логином это нормально. На больших объемах это быстро становится дорогим.

Команды, которые используют Node.js scraping libraries, часто хотят один инструмент для всех сайтов. Идея удобная, но обычно она оборачивается проблемами. Выбирайте самый легкий инструмент, который надежно достает данные, а потом доказывайте стоимость, прежде чем запускать тысячи запросов.

Что каждый вариант стоит на практике

С Node.js scraping libraries счет редко ограничивается только сервером. Инструмент может казаться дешевым на старте, но потом обходиться дороже, если он часто ломается или требует постоянного внимания.

HTML parsing обычно дешевле. Простой запрос плюс HTML parser могут обрабатывать большую партию страниц при умеренной нагрузке на CPU и RAM. На одной машине можно держать больше воркеров, быстро запускать задания и сохранять стабильные расходы в облаке. Если данные уже есть в исходном HTML, этот вариант трудно оспорить.

Headless-браузер меняет экономику. Каждый воркер должен запустить браузер, загрузить скрипты, отрисовать страницу и дождаться событий. Это добавляет время запуска к каждой задаче. На маленьком тесте задержка может казаться несущественной. На 5 000 страниц в день она уже заметна в счете.

Дополнительные расходы обычно возникают в нескольких местах:

  • Браузеры используют намного больше памяти на один воркер, поэтому нужны более мощные машины или меньше параллельных задач.
  • Они загружают больше ресурсов, что увеличивает трафик и расход прокси.
  • Сайты с антибот-защитой чаще запускают CAPTCHA для браузерного трафика.
  • Изменения верстки ломают браузерные сценарии чаще, чем простые HTML-селекторы.

Расходы на прокси и CAPTCHA могут расти очень быстро. Обычный запрос может получить один документ. Браузер часто загружает еще скрипты, изображения, шрифты и фоновые вызовы, даже если часть из них блокировать. Если сайт активно борется с автоматизацией, могут понадобиться residential proxies или сервисы решения CAPTCHA. И это иногда обгоняет расходы на вычисления.

Время разработчиков часто оказывается самой большой статьей расходов. Если senior-разработчик тратит полдня каждую неделю на ожидания, клики по кнопкам, всплывающие окна или сценарии входа, это обычно дороже, чем месяцы недорогого хостинга. Браузерные скрипты все еще могут быть правильным выбором, но им нужна более серьезная причина.

Простой принцип помогает не ошибиться: если данные есть в HTML, парсите HTML. Если страница собирает данные в браузере и иначе до них не добраться, платите за браузер и закладывайте поддержку заранее.

Простой пример для интернет-магазина

Постройте экономный pipeline
Соберите в один понятный процесс запросы, парсинг, повторы и запасные сценарии.

Представьте магазин электроники с 150 страницами категорий и 6 000 страницами товаров. На страницах категорий исходный HTML уже содержит название товара, цену и ссылку на товар. Обычный запрос плюс Cheerio могут собрать эти данные быстро, часто меньше чем за секунду на страницу, и с очень небольшим потреблением памяти.

Это работает, потому что сервер отдает данные до запуска скриптов. Если ноутбук стоит $899, Cheerio может прочитать эту цифру прямо из HTML и идти дальше. Для отслеживания цен, меток распродажи и количества товаров HTML parsing в Node.js обычно самый дешевый вариант.

На странице товара ситуация меняется. В сыром HTML может быть только заглушка вроде «Проверяем наличие». После загрузки страницы скрипт обращается к backend магазина за актуальным остатком, а потом меняет текст на «В наличии» или «Нет в наличии». Cheerio не увидит этот финальный текст, если вы не найдете и не вызовете тот же backend-endpoint сами.

Playwright может справиться с этой последней частью без догадок. Он открывает страницу, ждет обновления виджета наличия и читает результат так же, как его видит покупатель. Это стоит больше CPU, больше RAM и больше времени на страницу, поэтому использовать его для всего подряд обычно невыгодно.

Смешанный подход делает задачу разумной. Используйте Cheerio для страниц категорий, чтобы собрать списки, цены и ссылки на товары. А потом отправляйте в Playwright только те страницы товара, где нужна актуальная проверка наличия: например, товары с недавними изменениями цены, популярные позиции или страницы, где в прошлом запуске не хватило данных о наличии.

Многие Node.js scraping libraries имеют смысл только тогда, когда вы подбираете их под конкретную страницу. В одном проекте Cheerio делает дешевую массовую работу, а Playwright закрывает те части, которые появляются только после запуска скриптов. Вы тратите меньше, заканчиваете быстрее и все равно получаете нужные данные о наличии.

Ошибки, которые съедают дни

Самая распространенная ошибка — открывать headless-браузер раньше, чем вы убедились, что он действительно нужен. Если страница отдает данные в первом HTML-ответе, простой fetch плюс HTML parsing быстрее, дешевле и проще для отладки. Команды тратят часы на настройку Playwright или Puppeteer, а потом выясняют, что обычный запрос и Cheerio сделали бы работу в десять строк.

Еще одна медленная и болезненная ошибка — игнорировать API-вызовы, которые страница уже делает сама. Многие современные сайты сначала рендерят каркас, а потом получают товары, цены или отзывы из JSON-эндпоинтов. Если вместо вызова того же эндпоинта вы скрейпите итоговую страницу через браузер, вы платите за выполнение JavaScript, ожидания, повторы и дополнительные сбои. Один раз откройте DevTools, посмотрите Network и проверьте, не приходит ли нужная информация уже в чистом ответе.

Хрупкие селекторы ломаются первыми

Многие сломанные скрейперы начинаются с CSS-пути, скопированного прямо из DevTools. Он работает на одной странице, а потом перестает работать, когда сайт добавляет обертку div или меняет верстку под мобильный экран. Селекторы вроде #app \u003e div:nth-child(2) \u003e div \u003e ul \u003e li:nth-child(4) — это ловушка.

Ищите более стабильные ориентиры: class у карточки товара, который используется во всей сетке, data-*-атрибут или текст рядом с нужным полем. Если ничего похожего нет, возможно, нужен другой подход, например использование API-ответа вместо парсинга уже отрисованной страницы.

На маленьких машинах возникает другая проблема. Люди открывают 20 или 30 вкладок браузера параллельно на сервере с 2 vCPU, а потом удивляются, что память скачет, страницы начинают зависать и запуски становятся случайными. Headless browser scraping действительно стоит ресурсов. Меньше вкладок, очередь и понятные таймауты обычно лучше, чем попытка просто увеличить параллельность.

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

Храните четыре вещи для ошибок:

  • сырой HTML или JSON-ответ
  • скриншот для браузерных запусков
  • итоговый URL после редиректов
  • короткий лог с временем и статус-кодами

Такая привычка экономит дни. Когда скрейпер ломается в пятницу вечером, вам нужны факты, а не версии.

Быстрые проверки перед запуском

Помощь с автоматизацией
Если сбор данных — часть большего процесса, Олег поможет выстроить всю цепочку автоматизации.

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

Начните с проверки селекторов минимум на пяти реальных страницах. Выберите страницы с небольшими отличиями: одна со скидкой, одна без наличия, одна с большим числом отзывов, одна с пустыми полями и одна с другим шаблоном. Эта небольшая выборка быстро выявляет много неверных предположений.

Для Node.js scraping libraries этот шаг важнее, чем многие думают. Селектор, который отлично работает на чистой демо-странице, часто ломается, когда сайт добавляет бейдж, переносит блок или загружает одно поле позже остальных.

Используйте короткий checklist перед запуском:

  • Проверьте селекторы минимум на пяти примерах, а не на одном.
  • Задайте лимиты скорости до первого большого запуска и добавьте backoff после 429 или таймаутов.
  • Сохраняйте HTML с ошибкой для каждого сбоя. Если используете браузер, сохраняйте еще и скриншот.
  • С самого начала отслеживайте success rate, cost per page и число повторов.
  • Решите, когда скрипт должен остановиться и отправить страницу на ручную проверку.

Последний пункт экономит дни. Если страница трижды повторилась и все равно не прошла, хватит гадать. Отправьте ее в очередь на проверку. Человек обычно за две минуты замечает проблему: экран входа, региональную блокировку, сломанный селектор или поле, которое переехало в JavaScript.

Сделайте метрики простыми. Success rate показывает, здоров ли прогон. Cost per page показывает, когда headless browser scraping становится дорогим. Retry count показывает, проблема в сайте или в вашей логике.

Есть одно правило, которое стоит соблюдать жестко: никогда не запускайте большой скрейп без backoff и снимков ошибок. Если что-то пойдет не так, именно эти две вещи быстро объяснят, что произошло. Без них вы будете отлаживать вслепую.

Что делать дальше

Если вы выбираете среди Node.js scraping libraries, выбирайте под страницу перед вами, а не под стек, который команда уже знает. Обычный список товаров, новостная страница или страница результатов поиска часто отлично работают через простой fetch и HTML parsing. Сценарий оформления заказа, дашборд после входа или страница, которая строится после нескольких скриптов, обычно требуют браузер.

Начните с маленького теста на реальных страницах. Используйте 10–20 URL, а не один идеальный пример. Измерьте четыре вещи: success rate, время выполнения, потребление памяти и то, как часто ломаются селекторы. Такой небольшой тест скажет больше, чем длинный спор о Cheerio vs Playwright.

Хорошо работает простой план:

  • Сначала пробуйте fetch и HTML parsing на страницах со стабильной разметкой.
  • Переходите на headless-браузер, когда контент появляется только после JavaScript.
  • Сохраняйте сырой HTML или скриншоты для неудачных запусков, чтобы быстро разбираться в проблеме.
  • Держите селекторы простыми и привязанными к структуре страницы, а не к хрупким именам классов.
  • Добавьте запасной путь для страниц, которые меняются без предупреждения.

Запасной путь не обязан быть сложным. Если быстрый парсер не справился, повторите тот же URL в браузере для небольшой части запросов. Это удерживает расходы под контролем и дает страховку, когда сайт меняет верстку в пятницу вечером.

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

Если вашей команде нужен второй взгляд на компромиссы, Олег Сотников может проверить стек для сбора данных, стоимость запуска и риски отказов в рамках короткой CTO-консультации. Такой разбор особенно полезен до того, как вы масштабируете неудачный выбор на сотни страниц.