Парсер сайта на Python: как сделать и что ломается потом
Парсер сайта на Python в простом случае — это тридцать строк: скачать страницу, найти нужные блоки, записать в таблицу. За вечер такое собирает человек, который Python видел пару раз. Настоящая работа начинается дальше — когда данных нет в исходном коде страницы, когда сайт отвечает отказом на сотом запросе и когда через неделю выгрузка тихо становится пустой. Ниже — что чем решается и где именно проходит граница между «работает» и «работает каждый день».
Что делает парсер и когда нужен именно свой
Парсер делает три вещи подряд: получает страницу, вытаскивает поля, сохраняет результат. Всё остальное — обвязка вокруг этих шагов, и именно она съедает большую часть времени в реальном проекте.
Прежде чем писать код, потратьте десять минут на проверку, нужен ли парсер вообще: нет ли у сайта официального API, готового фида или выгрузки прайса. Иногда хватает письма поставщику — доступ дают бесплатно, им выгодно, чтобы ваши цены были актуальны. Парсинг HTML — последний вариант по надёжности: он читает то, что нарисовано для человека, и ломается от смены дизайна.
Самая полезная привычка: смотреть не на страницу в браузере, а на то, что реально прислал сервер. Видимое глазами и лежащее в ответе совпадает далеко не всегда, а от этого зависит и выбор инструмента, и трудоёмкость проекта.
Как сделать парсер сайта на Python: порядок действий
Пункты 1–3 решают судьбу проекта, а к коду дело доходит только на четвёртом.
- Проверить, где живут данные. Сохраните исходный код страницы и поищите в нём цену или название. Нашлись — задача простая. Не нашлись — значит, содержимое подгружается отдельно, и это другой сценарий, разобранный ниже.
- Собрать список адресов. Каталог с пагинацией, карта сайта, файл с идентификаторами товаров. Обход почти всегда двухуровневый: сначала список ссылок, потом карточки по одной.
- Определить поля и правила. Не «цена», а «цена в рублях, при отсутствии — пусто, при значении по запросу — отдельный признак». Эта минута экономит день разбора мусора в выгрузке.
- Скачать одну страницу и разобрать. Обязательно с таймаутом и проверкой кода ответа: 404 и 403 отличаются от успешной страницы только тем, что вы их не проверили.
- Прогнать двадцать страниц, не двадцать тысяч. На маленькой выборке видны все аномалии: товары без цены, карточки другого шаблона, редиректы.
- Сохранять сырые страницы на диск. Когда через неделю понадобится ещё одно поле, его достанут из скачанного, а не пойдут обходить сайт заново.
Про селекторы одно правило: цепляйтесь за смысл, а не за оформление. Идентификаторы, атрибуты данных и подписи полей живут годами, а классы из длинных случайных строк генерируются сборщиком и меняются при каждом обновлении дизайна.
Чем что решается: requests, BeautifulSoup, lxml, Playwright, Scrapy
Библиотеки не конкурируют — они закрывают разные слои: загрузка, разбор, масштаб.
| Инструмент | Для чего | Где упирается |
|---|---|---|
| requests | Обычная загрузка страниц, самый простой и понятный вариант | Синхронный, HTTP/1.1; таймаут по умолчанию отсутствует — запрос может ждать вечно, пока вы его не зададите |
| httpx | То же самое плюс асинхронные запросы и HTTP/2, знакомый интерфейс | Асинхронность усложняет код; нужна там, где сотни параллельных запросов |
| BeautifulSoup | Разбор HTML понятными методами поиска, терпим к кривой вёрстке | Скорость зависит от выбранного движка разбора |
| lxml | Быстрый разбор поверх библиотек на C, поддержка XPath | Синтаксис менее дружелюбный, чем у BeautifulSoup |
| Playwright | Настоящий браузер: страницы, где контент рисует JavaScript | На порядок тяжелее и медленнее HTTP-запроса, требует установки браузеров |
| Scrapy | Каркас для обхода десятков тысяч страниц: очередь, повторы, конвейер обработки | Свои правила и структура проекта; для одной страницы избыточен |
Рабочая связка для большинства задач: requests загружает, BeautifulSoup с движком lxml разбирает — читаемый код поиска и скорость библиотеки на C. Playwright подключают не потому, что он мощнее, а когда без браузера данных не достать. Scrapy оправдан на десятках тысяч страниц: в нём уже встроены очередь адресов, повторы при ошибках, ограничение частоты и автоподстройка задержки под скорость ответа сайта.
Полезная деталь Scrapy: в проекте, созданном штатной командой, соблюдение robots.txt включено по умолчанию, а задержка между запросами задаётся одной настройкой. В своём парсере обе вещи придётся закладывать руками — и забывают их чаще всего.
Пока читаете — можно сразу проверить свою задачу. Опишите процесс, и я скажу, решается ли он и во сколько обойдётся.
Когда данных нет в HTML: JSON на странице и внутренние запросы
Ситуация, на которой спотыкаются почти все: в браузере цена есть, а в скачанном коде её нет — страница получает данные уже после загрузки. Вариантов три, и по цене они различаются сильно.
- JSON внутри страницы. Часто данные лежат прямо в исходном коде — в разметке для поисковиков или в состоянии, которое сайт передаёт своему интерфейсу. Достаётся разбором JSON, без селекторов. Проверяйте первым делом.
- Внутренний запрос сайта. Интерфейс почти всегда получает данные отдельными обращениями к своим адресам — это видно на вкладке «Сеть». Повторить такой запрос из Python в десятки раз быстрее, чем запускать браузер. Оговорка: обязательств у такого адреса нет, он может измениться без предупреждения.
- Браузер через Playwright. Крайний вариант, когда без выполнения скриптов страницы данные не появляются. Работает всегда, но каждая страница стоит вам процессора и памяти, а обход большого каталога из минут превращается в часы.
Отдельная категория — защита от ботов: ограничение частоты, проверка заголовков, капча. Часть снимается вежливым поведением, часть не снимается вовсе, и понять это стоит до начала работы, а не на третий день. Сайты, требующие входа в аккаунт или обхода капчи, — другой разговор и другие риски.
Парсинг данных сайта на Python: где ломается на второй неделе
Парсер редко падает с ошибкой — чаще он продолжает работать и тихо отдаёт неправду. Что случается почти со всеми:
| Симптом | Что произошло | Что помогает |
|---|---|---|
| Выгрузка пустая, ошибок нет | Сайт обновил вёрстку, селекторы не находят поля | Проверка количества записей: упало вдвое — не перезаписывать данные, а прислать сообщение |
| Собралась только часть каталога | Пагинация закончилась раньше или каталог отдаёт лимит позиций | Идти до отсутствия следующей страницы, крупные разделы дробить фильтрами |
| Данные удвоились | Скрипт запустили повторно, строки добавились заново | Запись по ключу товара с обновлением, а не вставка новой строки |
| Сайт начал отвечать отказом | Слишком высокая частота запросов или подозрительные заголовки | Держать 1–2 запроса в секунду, уважать заголовок с временем ожидания, не ходить в много потоков |
| Задача висит сутки | Не задан таймаут, запрос ждёт ответа бесконечно | Таймаут на каждом запросе плюс общий предел времени на прогон |
| Никто не заметил, что парсер не работал | Сервер перезагрузили, задача не запустилась | Отметка об успешном завершении: нет отметки за сутки — приходит уведомление |
Общий знаменатель у всех шести пунктов один: отсутствие обратной связи. Парсер, умеющий сообщать о своём состоянии, чинится за полчаса; молчащий обнаруживают тогда, когда на его данных уже приняли решение.
Вежливый парсинг и правовые рамки
Отдельного закона о парсинге в России нет, и сбор открытых данных сам по себе не запрещён. Границы задают общие нормы: право изготовителя базы данных (ст. 1334 ГК РФ — об извлечении существенной части содержания), законодательство о персональных данных и статьи о неправомерном доступе. Вывод практический: сотня карточек для сравнения цен и выкачанный целиком чужой каталог — разные истории, а контакты физлиц лучше не собирать вовсе. Подробнее — в статье про парсер сайтов.
Технические правила по умолчанию — они же резко снижают шанс блокировки:
- Читать robots.txt. Не закон, а техническая просьба владельца. В стандартной библиотеке Python есть готовый разбор этого файла — он же подскажет рекомендованную паузу, если она указана.
- Держать 1–2 запроса в секунду. Такая нагрузка на порядки меньше обычного дневного трафика магазина и просто не заметна.
- Представляться в User-Agent. Название и контакт вместо маскировки. Тогда администратор напишет письмо, а не забанит подсеть.
- Уважать ответы сервера. Пришёл код 429 или указано время ожидания — ждём столько, сколько сказали, и увеличиваем паузу, а не долбим повторами.
- Кэшировать и ходить ночью. Повторно запрашивать страницу — только если она могла измениться.
- Брать только нужное. Нужны цены — забираем цены, а не весь сайт со всеми разделами заодно.
Когда заказывать не нужно
Часть, из-за которой я иногда отговариваю от разработки. Свой парсер не нужен, если:
- У источника есть API или готовый фид. Интеграция и дешевле, и не ломается от смены дизайна. Как это устроено — в разборе про парсинг данных через API.
- Задача разовая и небольшая. Двести строк один раз — час работы человека, разработка тут не окупится.
- Под источник есть готовый сервис. Подписка на пару тысяч рублей закроет вопрос быстрее любого кода.
- Вы хотите разобраться сами. Если цель — научиться, отдавать бессмысленно: вечер с документацией даст больше, чем готовый файл от подрядчика.
- Сайт явно против сбора. Обязательный вход, жёсткая капча, прямой запрет в условиях использования — это другой разговор и другие риски.
И наоборот: заказывать имеет смысл, когда источников несколько, данные нужны регулярно, а результат должен приезжать в вашу систему, а не в файл на рабочем столе. Платите вы тогда не за разбор HTML — за пагинацию, повторные запуски, проверки и уведомления, которых в вечернем скрипте нет по определению. Общие принципы таких скриптов — в статье про скрипты на языке Python.
Частые вопросы
Как сделать парсер сайта на Python с нуля?
Сначала проверьте, где лежат данные: сохраните исходный код страницы и поищите в нём нужное значение. Если оно там есть, хватит связки requests и BeautifulSoup с движком lxml. Дальше собирается список адресов, описываются правила для каждого поля и делается прогон на двадцати страницах, чтобы увидеть аномалии до полного обхода.
Что делать, если данных нет в исходном коде страницы?
Значит, содержимое подгружается отдельно. Откройте панель разработчика, вкладку «Сеть», и найдите запрос, который возвращает JSON, — повторить его из Python в десятки раз быстрее, чем запускать браузер. Playwright берут только тогда, когда без выполнения скриптов страницы данные не появляются вообще.
BeautifulSoup или lxml — что выбрать?
Это не альтернативы, а сочетание: BeautifulSoup даёт понятные методы поиска, lxml — скорость разбора на C. Обычно берут BeautifulSoup и указывают ему движок lxml. Чистый lxml с XPath имеет смысл, когда объёмы большие и важна каждая доля секунды.
Когда нужен Scrapy, а когда хватит обычного скрипта?
Обычного скрипта хватает до нескольких тысяч страниц с одного сайта. Scrapy оправдан, когда страниц десятки тысяч или источников много: в нём уже есть очередь адресов, повторы при ошибках, ограничение частоты и автоматическая подстройка задержки под скорость ответа сайта.
Как не получить блокировку при парсинге?
Держать 1–2 запроса в секунду, не ходить в много потоков, указывать в User-Agent название и контакт вместо маскировки и уважать ответ 429 с указанным временем ожидания. Плюс кэшировать уже скачанное, чтобы не запрашивать одну страницу дважды. Этого достаточно в подавляющем большинстве случаев.
Почему парсер собрал пустую таблицу без ошибок?
Почти всегда это смена вёрстки: селекторы больше не находят поля, но сам код отрабатывает штатно. Поэтому в парсере обязательна проверка результата — если записей стало заметно меньше обычного, он не перезаписывает данные, а присылает уведомление. Починка при вынесенных в конфиг правилах занимает от получаса.
Читайте дальше
Нужны данные с сайта?
Пришлите ссылку на источник и список полей — скажу, достаётся ли это простым запросом, нужен ли браузер и сколько займёт парсер по расписанию.
Обсудить задачу