Демо-проект
Парсер каталога: сайт в CSV и XLSX
Обход каталога по правилам robots.txt, сбор карточек товаров и выгрузка в CSV и XLSX.
разработка · Node.js · cheerio · ExcelJS

Задача
«Данные с сайта в таблицу» - один из самых частых запросов на фрилансе. У заказчика есть каталог: свой, партнёрский или поставщика. Нужна таблица, с которой работают в Excel: сверить цены, посчитать наценку, собрать прайс, загрузить на маркетплейс. Вручную это несколько часов однообразной работы и опечатки в ценах, которые находятся уже после выгрузки.
Стенд показывает эту механику от начала до конца. Источник - собственный демо-магазин PrintCraft, поднятый локально на статике: компания вымышленная, структура каталога настоящая. Чужие сайты в стенд не берутся намеренно. Один robots.txt не закрывает правовую сторону сбора данных, но прочитать его и послушаться - первое, что делают на настоящем заказе, и здесь это сделано: правила разбираются до первого запроса к каталогу.
Решение
Прогон - четыре шага: правила, обход, разбор карточек, выгрузка.
Перед первым запросом парсер читает robots.txt: выбирает подходящую группу правил - по имени своего агента или общую User-agent: * - и разбирает запреты, разрешения и паузу Crawl-delay. Спор двух правил решается их длиной: побеждает более длинное, при равной длине - разрешение. Запреты печатаются в журнал, запрещённые адреса пропускаются с указанием правила: на замере 24 августа один адрес был отброшен по Disallow: /cart/. Между запросами выдерживается пауза - большая из требования сайта и собственной минимальной. За пределы своего хоста обход не выходит: внешние ссылки идут в отдельный счётчик и не запрашиваются.
С карточки товара снимаются восемь полей: название, категория из хлебных крошек, цена, наличие, материал, доступные цвета, краткое описание и адрес страницы. Карточка без названия или без цены в таблицу не попадает - она отмечается в журнале с перечнем недостающих полей, а прогон идёт дальше.
На выходе за один прогон два файла. catalog.csv - UTF-8 с BOM и точкой с запятой: кириллица открывается без кракозябр, а там, где системный разделитель списка и есть точка с запятой, Excel открывает файл двойным щелчком, без мастера импорта. Под другую локаль разделитель меняется ключом --sep. catalog.xlsx - лист «Каталог» с закреплённой шапкой, автофильтром, заданной шириной колонок и переносом текста в колонке описания.
Детали, которые легко не заметить
- Цена в XLSX лежит числом, а не текстом. Формат
# ##0, выравнивание вправо. По колонке сразу считается сумма и работает сортировка - иначе таблицу пришлось бы чистить руками, а это съедает всю сэкономленную работу. - Колонка «Наличие» не выдумывается. У демо-магазина нет отдельного признака остатка, поэтому парсер судит по состоянию кнопки «В корзину» и по явным словам на странице, а основание записывает в журнал: кнопка «В корзину» активна, кнопка «В корзину» отключена, текст страницы, кнопки заказа на странице нет. В последнем случае в таблицу идёт «нет данных», а не «нет в наличии»: видно, чему верить.
- Сбой сети не роняет прогон. Ответы 408, 425, 429 и 5xx, таймауты и обрывы соединения повторяются: по умолчанию на адрес три попытки, то есть два повтора с паузами 400 и 800 мс. Дальше отказ пишется в журнал, а обход продолжается со следующего адреса. Число попыток и базовая пауза задаются ключами
--attemptsи--backoff. - Отказы проверены на живом сервере, а не на заглушке. Чистый прогон по демо-магазину не даёт ни одного повтора, поэтому механику отказов проверяет отдельный сценарий: он поднимает сервер, который действительно отвечает 503, действительно рвёт соединение и действительно молчит дольше таймаута. Он же читает записанные таблицы обратно с диска - проверяет BOM, экранирование разделителя внутри значения, тип ячейки с ценой и закреплённую шапку.
- Каждый прогон оставляет след. Журнал
journal.jsonlпишет построчно: правила robots, каждую страницу со статусом и временем ответа, каждый найденный товар, обе выгрузки и итог. Абсолютных путей в нём нет, только имена файлов: журнал можно показать заказчику как есть.
Цифры
Замер 24 августа, журнал лежит рядом с кодом. Обойдено 13 страниц сайта, 9 из них - карточки товаров, и все 9 попали в таблицу. Один адрес пропущен по Disallow: /cart/, повторов 0, запросов без ответа 0. Прогон занял 12,3 секунды, из которых 12 - выдержанная пауза по Crawl-delay: 1; на сами запросы ушло 128 миллисекунд. Скорость здесь упирается не в код, а в вежливость к сайту.
Скриншоты


