Парсер сайта на Python: с чего начать и где грабли
← Все материалы
Гайд

Парсер сайта на Python: как сделать и что ломается потом

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

1 вечердо первой выгрузки с одной страницы
1–2 запр/сскорость, которую сайт не замечает
3–10 днейдо парсера, работающего по расписанию

Что делает парсер и когда нужен именно свой

Парсер делает три вещи подряд: получает страницу, вытаскивает поля, сохраняет результат. Всё остальное — обвязка вокруг этих шагов, и именно она съедает большую часть времени в реальном проекте.

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

Открыть страницуПосмотретьисходный кодПроверить, естьли JSONНаписать разборполейСохранитьрезультат
Половина решений в проекте принимается до первой строки кода — в панели разработчика браузера

Самая полезная привычка: смотреть не на страницу в браузере, а на то, что реально прислал сервер. Видимое глазами и лежащее в ответе совпадает далеко не всегда, а от этого зависит и выбор инструмента, и трудоёмкость проекта.

Как сделать парсер сайта на Python: порядок действий

Пункты 1–3 решают судьбу проекта, а к коду дело доходит только на четвёртом.

  1. Проверить, где живут данные. Сохраните исходный код страницы и поищите в нём цену или название. Нашлись — задача простая. Не нашлись — значит, содержимое подгружается отдельно, и это другой сценарий, разобранный ниже.
  2. Собрать список адресов. Каталог с пагинацией, карта сайта, файл с идентификаторами товаров. Обход почти всегда двухуровневый: сначала список ссылок, потом карточки по одной.
  3. Определить поля и правила. Не «цена», а «цена в рублях, при отсутствии — пусто, при значении по запросу — отдельный признак». Эта минута экономит день разбора мусора в выгрузке.
  4. Скачать одну страницу и разобрать. Обязательно с таймаутом и проверкой кода ответа: 404 и 403 отличаются от успешной страницы только тем, что вы их не проверили.
  5. Прогнать двадцать страниц, не двадцать тысяч. На маленькой выборке видны все аномалии: товары без цены, карточки другого шаблона, редиректы.
  6. Сохранять сырые страницы на диск. Когда через неделю понадобится ещё одно поле, его достанут из скачанного, а не пойдут обходить сайт заново.

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

Чем что решается: 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 вответеПовторить его изPythonГотовые поля безразбора HTML
Самый выгодный путь: данные приходят структурой, а не текстом, и не ломаются от смены дизайна

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

Парсинг данных сайта на Python: где ломается на второй неделе

Парсер редко падает с ошибкой — чаще он продолжает работать и тихо отдаёт неправду. Что случается почти со всеми:

СимптомЧто произошлоЧто помогает
Выгрузка пустая, ошибок нетСайт обновил вёрстку, селекторы не находят поляПроверка количества записей: упало вдвое — не перезаписывать данные, а прислать сообщение
Собралась только часть каталогаПагинация закончилась раньше или каталог отдаёт лимит позицийИдти до отсутствия следующей страницы, крупные разделы дробить фильтрами
Данные удвоилисьСкрипт запустили повторно, строки добавились зановоЗапись по ключу товара с обновлением, а не вставка новой строки
Сайт начал отвечать отказомСлишком высокая частота запросов или подозрительные заголовкиДержать 1–2 запроса в секунду, уважать заголовок с временем ожидания, не ходить в много потоков
Задача висит суткиНе задан таймаут, запрос ждёт ответа бесконечноТаймаут на каждом запросе плюс общий предел времени на прогон
Никто не заметил, что парсер не работалСервер перезагрузили, задача не запустиласьОтметка об успешном завершении: нет отметки за сутки — приходит уведомление

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

СКРИПТ НА ВЕЧЕРПАРСЕР НА КАЖДЫЙ ДЕНЬСелекторы прописаны в кодеПовторный запуск дублирует строкиОдна ошибка обрывает весь прогонРезультат — файл на рабочем столеО поломке узнаёте от коллегПравила разбора вынесены в конфигПовтор за тот же период безопасенСбойная страница откладывается, обход идёт …Данные в базе, файл — только витринаСбой сразу приходит сообщением
Ни один пункт справа не про сложный код — все про предсказуемость
Первая рабочая версия≈ вечерПагинация, повторы, дедуп…≈ 2 дняРасписание, логи, уведомл…≈ 2–3 дня
Ориентировочная трудоёмкость: «работает» и «работает каждый день» — разные проекты

Вежливый парсинг и правовые рамки

Отдельного закона о парсинге в России нет, и сбор открытых данных сам по себе не запрещён. Границы задают общие нормы: право изготовителя базы данных (ст. 1334 ГК РФ — об извлечении существенной части содержания), законодательство о персональных данных и статьи о неправомерном доступе. Вывод практический: сотня карточек для сравнения цен и выкачанный целиком чужой каталог — разные истории, а контакты физлиц лучше не собирать вовсе. Подробнее — в статье про парсер сайтов.

Технические правила по умолчанию — они же резко снижают шанс блокировки:

  1. Читать robots.txt. Не закон, а техническая просьба владельца. В стандартной библиотеке Python есть готовый разбор этого файла — он же подскажет рекомендованную паузу, если она указана.
  2. Держать 1–2 запроса в секунду. Такая нагрузка на порядки меньше обычного дневного трафика магазина и просто не заметна.
  3. Представляться в User-Agent. Название и контакт вместо маскировки. Тогда администратор напишет письмо, а не забанит подсеть.
  4. Уважать ответы сервера. Пришёл код 429 или указано время ожидания — ждём столько, сколько сказали, и увеличиваем паузу, а не долбим повторами.
  5. Кэшировать и ходить ночью. Повторно запрашивать страницу — только если она могла измениться.
  6. Брать только нужное. Нужны цены — забираем цены, а не весь сайт со всеми разделами заодно.

Когда заказывать не нужно

Часть, из-за которой я иногда отговариваю от разработки. Свой парсер не нужен, если:

И наоборот: заказывать имеет смысл, когда источников несколько, данные нужны регулярно, а результат должен приезжать в вашу систему, а не в файл на рабочем столе. Платите вы тогда не за разбор 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 с указанным временем ожидания. Плюс кэшировать уже скачанное, чтобы не запрашивать одну страницу дважды. Этого достаточно в подавляющем большинстве случаев.

Почему парсер собрал пустую таблицу без ошибок?

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

парсер сайта на pythonкак сделать парсер сайта на pythonпарсинг данных сайта pythonпарсинг данных на pythonrequests и beautifulsoup

Читайте дальше

Нужны данные с сайта?

Пришлите ссылку на источник и список полей — скажу, достаётся ли это простым запросом, нужен ли браузер и сколько займёт парсер по расписанию.

Обсудить задачу