Как создать надежный парсер списка с помощью Python и Nstproxy
TL;DR
Краулинг списков извлекает повторяющиеся записи через контролируемую последовательность страниц. Последовательность может использовать нумерованные страницы, ссылку "далее", курсор, действие "загрузить больше" или предопределенную очередь URL.
BeautifulSoup является наиболее понятным вариантом для небольшого статического списка. Он дает вам прямой контроль, но вам нужно реализовать запросы, пагинацию, повторы, дедупликацию, контрольные точки и экспорты.
Scrapy является более сильной основой Python для многократной работы с многостраничными документами. Его планировщик, промежуточное ПО для повторов, экспорт данных, статистика и возобновляемые задачи убирают код инфраструктуры, не удаляя обслуживание селекторов.
Nstproxy Crawl подходит, когда получение данных является более сложной задачей. Он предоставляет ограниченное обнаружение, рендеринг JavaScript, маршрутизацию прокси и выводы страниц, в то время как ваш конвейер сохраняет ответственность за идентификацию элементов и их проверку.
Схема не гарантирует корректность данных. Принятие в производственных условиях также должно проверять происхождение, уровень дубликатов, полноту обязательных полей, завершение пагинации и неожиданные распределения значений.
Введение: краулинг списков — это проблема управления состоянием
Краулинг списков выглядит как упражнение с селекторами до первого повтора, накладывающейся страницы, тихой замены шаблона или бесконечного цикла курсора. Надежный краулер должен знать, какие страницы были попытаны, какие записи были приняты, почему запись была отклонена и с какого места нужно продолжить. Этот гид тестирует два подхода Python против общедоступного песочницы для скрапинга, затем показывает, где Nstproxy Crawl может заменить уровень доступа и рендеринга.
Примеры намеренно останавливаются после двух страниц. Этот ограниченный запуск достаточно для проверки селекторов и управления потоком без превращения учебника в ненужную задачу по сбору данных с всего сайта.
Что такое краулинг списков?
Краулинг списков — это многократное обнаружение и извлечение записей с похожей структурой с страниц списков или предопределенной очереди URL. Типичные цели включают продуктовые сетки, публичные каталоги, индексы статей, календари событий и авторизованные списки вакансий. Каждая запись обычно содержит как бизнес-поля, так и поля происхождения, такие как , , , и .
Краулинг списков сочетает две задачи, которые должны оставаться отдельными. Краулинг находит следующую страницу или URL; скрапинг преобразует каждую страницу в записи. Это различие объясняется подробнее в различиях между веб-скрапингом и веб-краулингом.
Пагинация определяет форму очереди. Нумерованные страницы раскрывают индекс, пагинация с ссылкой "далее" раскрывает цепочку, API с курсорами раскрывают непрозрачное состояние, а бесконечный прокрутка часто раскрывает либо фоновый запрос, либо действие браузера. Глоссарий по пагинации охватывает эти механизмы более подробно.
Почему краулинг списков терпит неудачу после успешной демонстрации
Краулинг списков терпит неудачу в производстве, потому что загруженная страница не является той же записью, которая была принята. Ответ может возвращать статус успешного HTTP, показывая экран согласия, мягкую ошибку, пустую оболочку приложения или повторяющуюся страницу. Парсер также может возвращать правдоподобный, но неверный текст после изменения макета.
Первый тест на Python выявил меньшую версию этой проблемы: тело сервера объявило UTF-8, но первоначальная кодировка в Requests привела к модулированию в валюте и пунктуации. Установка кодировки ответа по обнаруженному контенту исправила текст. Это такой дефект, который пропускает счетчик "найденных записей".
Перед краулингом проверьте условия использования сайта, опубликованную политику и официальные опции API. Документация Протокола исключения роботов Google объясняет, как правила применяются к конкретному хосту, протоколу и порту. Правила для роботов регулируют сигналы доступа краулеров; они не предоставляют права на повторное использование личных, защищенных авторским правом или ограниченных данных.
Какой метод краулинга списков вам выбрать?
Выбирайте метод в зависимости от оперативной ответственности, а не по длине кода.
Ограниченное обнаружение сайта или отправленная цель обрабатываются службой
Страницы JavaScript
Требуется отдельный браузер
Требуется интеграция рендеринга
Рендеринг браузера доступен как опция краулинга
Модель извлечения
CSS/тег парсинг на ваш выбор
CSS/XPath парсинг и пайплайны на ваш выбор
Выводы страниц управляются; точные бизнес-записи все еще нуждаются в проверке
Перезапуск и наблюдаемость
Создайте контрольные точки и метрики
Статистика плюс поддержка постоянных задач
Записи краулинга и прогресс задач управляются; состояние записей ниже остается вашим
Обслуживание поверхностей
HTTP, парсинг, повторные попытки, хранилище
Селекторы, правила пауков, конвейеры, развертывание
Конфигурация обхода, парсер записей, тесты схемы и интеграция с поставщиками
Наилучшее применение
Малые статические списки и прототипы
Периодические краулеры на Python с пользовательской логикой
Динамические или чувствительные к доступу сайты, где инфраструктура краулеров является узким местом
Документация по Beautiful Soup определяет библиотеку как инструмент для парсинга HTML/XML, а не как планировщик или загрузчик. Scrapy охватывает больше жизненного цикла обхода. Nstproxy Crawl охватывает извлечение, отображение, ограниченное обнаружение, маршрутизацию и вывод на уровне страниц, но не должен рассматриваться как замена для проверки доменов.
Подробный учебник
Учебник использует https://books.toscrape.com/catalogue/page-1.html, публичный песочницу, созданную для практики в парсинге. Оба метода на Python были выполнены с актуальными пакетами и вернули 40 уникальных записей с двух страниц списка.
Метод 1: обход статического списка с BeautifulSoup
BeautifulSoup подходит, когда записи и следующая ссылка присутствуют в начальном HTML, а запуск достаточно мал, чтобы обработать его в одном процессе.
Шаг 1: установить и определить контракт записи
Установите requests и beautifulsoup4. В примере сохраняется сырой текст цены вместо преобразования в число, поскольку парсинг валюты является отдельным правилом для доменов. Он выводит стабильный ID элемента из канонического детального пути и сохраняет URL исходного списка для отслеживаемости.
Документация по Requests рекомендует явно задавать тайм-ауты для производственных запросов и различает успешное декодирование JSON от успешного HTTP-ответа. Та же дисциплина применяется и к HTML.
Шаг 2: следовать по следующей ссылке и отклонять повторяющиеся страницы
import hashlib, json
from datetime import datetime, timezone
from urllib.parse import urljoin, urlsplit
import requests
from bs4 import BeautifulSoup
next_url ="https://books.toscrape.com/catalogue/page-1.html"max_pages =2session = requests.Session()session.headers["User-Agent"]="Nstproxy-list-crawl-tutorial/1.0"seen_items, seen_pages ={},set()run_time = datetime.now(timezone.utc).isoformat()pages =0while next_url and pages < max_pages: response = session.get(next_url, timeout=(5,20)) response.raise_for_status() response.encoding = response.apparent_encoding
soup = BeautifulSoup(response.text,"html.parser") cards = soup.select("article.product_pod")ifnot cards:raise RuntimeError(f"На {response.url} не найдено карточек товаров") page_ids =[]for card in cards: link = card.select_one("h3 a[href]") detail_url = urljoin(response.url, link["href"]) parts =[p for p in urlsplit(detail_url).path.split("/")if p] record ={"item_id": parts[-2],"title": link["title"].strip(),"detail_url": detail_url,"list_url": response.url,"price_text": card.select_one("p.price_color").get_text(strip=True),"observed_at": run_time,} seen_items[record["item_id"]]= record
page_ids.append(record["item_id"]) fingerprint = hashlib.sha256("\n".join(sorted(page_ids)).encode()).hexdigest()if fingerprint in seen_pages:raise RuntimeError(f"Обнаружена повторяющаяся страница на {response.url}") seen_pages.add(fingerprint) pages +=1 next_link = soup.select_one("li.next a[href]") next_url = urljoin(response.url, next_link["href"])if next_link elseNonefor record in seen_items.values():print(json.dumps(record, ensure_ascii=False))print({"pages": pages,"accepted_records":len(seen_items)})
Шаг 3: интерпретировать результат перед масштабированием
Подтвержденный запуск вернул {"pages": 2, "accepted_records": 40}. Это доказывает, что селекторы и переход по следующей ссылке работали для выбранных страниц. Это не доказывает, что каждая последующая страница имеет тот же шаблон, что цены семантически валидны или что будущий запуск будет идентичным.
Перед увеличением max_pages сохраните записи в таблицу с ограничением уникальности по item_id, сохраните последнюю завершенную страницу после успешного коммита и уведомляйте, если полнота требуемых полей или количество записей на странице резко изменяется.
Метод 2: переместите очередь в Scrapy
Scrapy подходит, когда вам нужен планировщик, промежуточное ПО повторных попыток, экспорт, статистика обхода и путь к постоянным задачам, сохраняя при этом селекторы и конвейеры на Python.
Шаг 1: выразить записи и пагинацию как вывод паука
Установите scrapy, сохраните этого паука и поддерживайте охрану из двух страниц во время проверки:
from urllib.parse import urlsplit
import scrapy
classBookListSpider(scrapy.Spider): name ="book_list" start_urls =["https://books.toscrape.com/catalogue/page-1.html"] custom_settings ={
{"ROBOTSTXT_OBEY":true,"DOWNLOAD_DELAY":0.5,"CONCURRENT_REQUESTS_PER_DOMAIN":2,"RETRY_TIMES":2,"LOG_LEVEL":"INFO"}def parse(self, response): cards = response.css("article.product_pod")
if not cards: self.logger.error("Нет карточек продуктов на %s", response.url)
return
for card in cards: detail_url = response.urljoin(card.css("h3 a::attr(href)").get())
parts = [p for p in urlsplit(detail_url).path.split("/") if p] yield {"item_id": parts[-2],"title": card.css("h3 a::attr(title)").get().strip(),"detail_url": detail_url,"list_url": response.url,"price_text": card.css("p.price_color::text").get().strip(),} page = int(response.url.rsplit("-",1)[-1].split(".")[0])
next_href = response.css("li.next a::attr(href)").get()
if next_href and page < 2: yield response.follow(next_href, callback=self.parse)
}### Шаг 2: запустите безопасный тестовый экспорт
Запустите `scrapy runspider list_scrapy.py -O books.jl`. Проверенный запуск использовал Scrapy 2.13.4, получил две страницы списка, экспортировал 40 записей в формате JSON Lines и завершился нормально. Дополнительный запрос был связан с отсутствующим `robots.txt`, который вернул ответ "не найден"; это наблюдение должно быть зафиксировано в журнале выполнения, а не игнорироваться молча.
### Шаг 3: добавьте возможность возобновления и контроль качества данных
Для запланированного краулера используйте постоянный каталог задач Scrapy, а не рассматривайте выходные файлы как контрольные точки. Документация о <a href="https://docs.scrapy.org/en/latest/topics/jobs.html" rel="nofollow noopener"><strong>задачах Scrapy</strong></a> описывает приостановку и возобновление обхода с помощью `JOBDIR` и предупреждает, что один каталог должен принадлежать только одной задаче.
Добавьте конвейер элементов, который нормализует поля, выполняет upsert по `item_id` и отвергает отсутствующие обязательные значения. Экспортируйте статистику паука для мониторинга. Завершенный паук считается операционно успешным только тогда, когда количество принятых записей и распределение полей также проходят.
<div style="background-color: #f3f4f6; padding: 24px 40px; border-radius: 10px;">
<p style="font-weight: bold; font-size: 18px; margin-bottom: 10px;">
Быстрый Обзор
</p>
<p style="margin-bottom: 24px;">
Протестируйте один репрезентативный путь списка в Nstproxy Crawl перед расширением границ. Сравните полноту рендеринга страницы, обнаруженные URL и принятые записи с базовой линией Python.
</p>
<div style="text-align: center; margin-top: 10px;">
<a href="https://app.nstproxy.com/auth/login?utm_source=official&utm_medium=blog&utm_campaign=/list-crawling/" style="background: #1e4dff; color: #fff; padding: 10px 28px; border-radius: 38px; text-decoration: none; display: inline-block;">
Настроить Nstproxy
</a>
</div>
</div>
### Метод 3: Используйте Nstproxy Crawl для доступа и рендеринга
[Nstproxy Crawl](https://www.nstproxy.com/scraping) подходит, когда рендеринг JavaScript, маршрутизация через прокси, ограниченное открытие или операции краулера требуют больше усилий, чем парсер записей. Текущая поверхность продукта поддерживает рабочий процесс PlayGround и API, ограничения глубины и страниц, правила включения/исключения, рендеринг в браузере, опции прокси и выводы страниц в формате Markdown, HTML, JSON, ссылок или PDF. Оплата производится на основе использования за каждый успешно обработанный URL; цены намеренно опущены, поскольку они меняются. Это отлично подходит для команд, которым необходимо управлять получением и открытием страниц, сохраняя при этом логику схемы в своем собственном конвейере. Это не обещание, что каждая страница доступна или что каждое возвращенное поле верно.### Шаг 1: представьте репрезентативный путь списка
Откройте [Nstproxy Crawl](https://www.nstproxy.com/scraping) и начните с одной общедоступной категории или URL-адреса директории. Используйте PlayGround перед генерацией кода API. Обзор запуска [Nstproxy Crawl](https://www.nstproxy.com/blog/nstproxy-crawl-launch) предоставляет дополнительный контекст рабочего процесса.### Шаг 2: установите границы перед включением рекурсии
Установите небольшое максимальное количество страниц и глубину, включайте только релевантные пути списка/подробностей и исключайте корзины, учетные записи, календари, дубли с градациями и файлы. Включайте JavaScript только если необходимые записи отсутствуют в необработанном HTML. Эти выборы ограничивают случайное расширение URL и делают тестовый запуск объяснимым.
### Шаг 3: проверьте выходные страницы как входные, а не как истину
Выберите самый легкий формат страницы, который сохраняет необходимые вашему парсеру поля. Затем примените те же проверки `item_id`, происхождения, дубликатов и полноты, которые используются методами Python. [Словарь структурированных данных](https://www.nstproxy.com/glossary/structured-data) объясняет, почему типизированная форма полезна, но типовая валидность все равно не может доказать, что значение относится к правильному элементу.
Живая страница продукта была проверена для этого руководства, но аутентифицированное сканирование не было выполнено, так как учетные данные не были доступны. Рассматривайте метод Nstproxy как задокументированный операционный путь, пока ваш собственный репрезентативный запуск не пройдет те же приемочные ворота.
Что большинство руководств по сканированию списков упускает
Сканирование списков в производстве требует явных конечных состояний. Каждая страница или URL должны заканчиваться как принятые, постоянно отклоненные или повторяемые с ограниченным количеством попыток. "Нет следующей ссылки" само по себе недостаточно: также останавливайтесь на повторяющемся курсоре, повторяющемся отпечатке страницы, лимите страниц или сроке.
Проверка схемы необходима, но не полная. Модель может принять синтаксически корректный заголовок, взятый из неправильной карты, стандартную цену, скопированную через каждую строку, или атрибут, извлеченный с помощью LLM, который не поддерживается страницей. Храните поля доказательства, примеры необработанных выходных данных и сравнивайте распределения между запусками.
Используйте эти метрики приемлемости:
кандидатные записи против принятых записей;
полнота обязательных полей по полям;
коэффициент дубликатов после канонизации;
отдельный успех страницы списка и успех страницы деталей;
страницы, которые не добавляют невидимых идентификаторов элементов;
отставание свежести и использование сканирования на каждую принятую запись.
Также отделяйте обнаружение списка от обогащения деталей. Если одна страница деталей не прошла, сохраните запись обнаружения с состоянием обогащения. В противном случае частый сбой обогащения может стереть доказательства того, что элемент существовал.
Заключение: выберите границу владения, с которой вы можете работать
BeautifulSoup является самым ясным инструментом для обучения с ограниченным статическим списком, тогда как Scrapy является лучшей базой на Python, когда важны планирование, повторные попытки, экспорт и возможность возобновления. Выбирайте Nstproxy Crawl, когда рендеринг в браузере, маршрутизация прокси и ограниченное обнаружение страниц являются операционными узкими местами, затем сохраняйте стабильные идентификаторы, проверку, контрольные точки и метрики приемлемости в вашем уровне данных. Начните с двух репрезентативных страниц, докажите контракт записи и расширяйте только после того, как сканер сможет остановиться и возобновиться предсказуемо.
Для повторяющихся сканирований, которые объединяют несколько прокси-источников и требуют централизованной видимости маршрутизации, также оцените Nstproxy Proxy Manager.
Попробуйте Nstproxy — начните свою бесплатную пробную версию сегодня
Запустите ограниченное образцовое сканирование списка, сравните его с вашим базовым уровнем Python и масштабируйте только тогда, когда принятые записи — не просто ответы страниц — пройдут проверку.
В: Является ли сканер списка тем же самым, что и веб-скрейпер?
Нет. Сканер списка управляет обнаружением страниц или URL и их состоянием, в то время как скрейпер извлекает поля; практические конвейеры сканирования списков выполняют обе задачи.
В: Должен ли я использовать BeautifulSoup или Scrapy?
Используйте BeautifulSoup для небольшого статического рабочего процесса, где вам нужно явное управление, и используйте Scrapy, когда планирование, повторные попытки, экспорт, статистика сканирования и возможность возобновления оправдывают использование фреймворка.
В: Как сканер должен останавливать пагинацию?
Скомбинируйте сигналы: нет следующего токена, нет невидимых идентификаторов элементов, нет повторяющегося отпечатка и принудительные ограничения по страницам и времени. Один пустой ответ обычно должен быть исследован, а не восприниматься как доказательство завершения.
В: Устраняет ли извлечение на основе схемы очистку данных?
Нет. Схема может обеспечивать форму и типы, но вам все равно нужны происхождение, канонизация, дедупликация, семантическая проверка и мониторинг изменений.
В: Когда следует включать рендеринг в браузере?
Включайте рендеринг в браузере только тогда, когда отсутствуют требуемые записи или элементы управления пагинацией в исходном HTML и появляются после выполнения JavaScript.
В: Является ли сканирование списков законным?
Законность зависит от данных, юрисдикции, метода доступа, условий сайта и предполагаемого использования. Предпочитайте официальные API, когда это возможно, уважайте опубликованные условия сканирования, минимизируйте сбор и получайте юридическое заключение для чувствительных или коммерческих случаев использования.
Lena Zhou
Aug. 11th 2026
Попробуйте Nstproxy - Начните бесплатный тест сегодня
110M+ реальных IP с 99.9% успешных доступов
Мгновенный доступ к премиальным residential, datacenter, IPv6 и ISP пулам.
Средний отклик ~0.5с для задач высокой конкуренции