Перейти к содержанию

Архитектура пауков

Что нужно знать

  1. Прочитайте страницу Основы Fetcher, чтобы узнать о разных типах Fetcher и понять, когда их использовать.
  2. Прочитайте страницу Основные классы, чтобы познакомиться с классами Selector и Response.

Система пауков Scrapling представляет собой асинхронный фреймворк для обхода сайтов, вдохновлённый Scrapy. Она поддерживает одновременные запросы, несколько сессий, паузу и возобновление работы. Система объединяет парсер и Fetcher в общий API для обхода, а также добавляет планирование запросов, управление их одновременным выполнением и контрольные точки.

Если вы знакомы со Scrapy, многие приёмы будут привычны. Если нет, система всё равно проста в освоении.

Поток данных

На схеме показан путь данных в системе пауков во время обхода:

Схема архитектуры паука от @TrueSkills

Вот что происходит при запуске паука, без лишних подробностей:

  1. Паук создаёт первую группу объектов Request. По умолчанию для каждого URL в start_urls создаётся один запрос. Для своей логики можно переопределить start_requests().
  2. Планировщик принимает запросы, помещает их в очередь с приоритетами и создаёт их отпечатки. Запросы с более высоким приоритетом извлекаются первыми.
  3. Движок обхода просит планировщик выдать следующий запрос, соблюдая общие ограничения числа одновременных запросов, ограничения для каждого домена и задержки загрузки. Если включён robots_txt_obey, движок сначала проверяет правила robots.txt домена. Запрещённые запросы отбрасываются без уведомления. Полученный запрос движок обхода передаёт менеджеру сессий, который выбирает сессию по полю sid запроса (ID сессии).
  4. Сессия загружает страницу и возвращает объект Response движку обхода. Движок собирает статистику и проверяет, не заблокирован ли ответ. При блокировке запрос повторяется до max_blocked_retries раз. Обнаружение блокировок и логику повторных попыток можно настроить.
  5. Движок обхода передаёт Response обработчику запроса. Обработчик выдаёт либо словарь с извлечённой записью, либо следующий запрос, который передаётся планировщику и попадает в очередь.
  6. Цикл повторяется с шага 2, пока очередь не опустеет и не завершатся все задачи, либо пока паук не будет поставлен на паузу.
  7. Если при запуске задан crawldir, движок обхода периодически сохраняет на диск контрольную точку: ожидающие запросы и множество уже встреченных URL. При корректной остановке (Ctrl+C) сохраняется последняя контрольная точка. При следующем запуске с тем же crawldir паук продолжает работу с места остановки: пропускает start_requests() и восстанавливает состояние планировщика.

Компоненты

Spider

Основной класс, с которым вы работаете. Создайте подкласс Spider, задайте start_urls и метод parse(). При необходимости настройте сессии и переопределите хуки жизненного цикла.

from scrapling.spiders import Spider, Response, Request

class MySpider(Spider):
    name = "my_spider"
    start_urls = ["https://example.com"]

    async def parse(self, response: Response):
        for link in response.css("a::attr(href)").getall():
            yield response.follow(link, callback=self.parse_page)

    async def parse_page(self, response: Response):
        yield {"title": response.css("h1::text").get("")}

Движок обхода

Движок управляет всем обходом: главным циклом, ограничениями одновременных запросов, отправкой запросов через менеджер сессий и обработкой результатов. Напрямую работать с ним не нужно. Это делают методы Spider.start() и Spider.stream().

Планировщик

Очередь с приоритетами и встроенным удалением повторных URL. Отпечаток запроса строится на основе URL, HTTP-метода, тела и ID сессии. Планировщик поддерживает snapshot() и restore() для системы контрольных точек, чтобы сохранять состояние обхода и возобновлять его.

Менеджер сессий

Управляет одним или несколькими именованными экземплярами сессий. Каждая сессия имеет один из типов:

При получении запроса менеджер выбирает сессию по полю sid. Сессии могут запускаться вместе с пауком (по умолчанию) или при первом использовании.

Система контрольных точек

Необязательная система, которая сохраняет состояние краулера в pickle-файл на диске: ожидающие запросы и отпечатки уже встреченных URL. Запись атомарная: сначала во временный файл, затем переименование. Это защищает от повреждения данных. Контрольные точки сохраняются с заданным интервалом и при корректной остановке. После успешного завершения обхода, а не паузы, файлы удаляются автоматически.

Кеш ответов

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

Результаты

Извлечённые записи собираются в ItemList: подкласс списка с методами экспорта to_json(), to_jsonl(), to_csv() и to_xml(). Статистика обхода хранится в dataclass CrawlStats и содержит много полезных данных.

Сравнение со Scrapy

Если вы переходите со Scrapy, эта таблица поможет сопоставить возможности:

Понятие Scrapy Scrapling
Определение паука Подкласс scrapy.Spider Подкласс scrapling.spiders.Spider
Начальные запросы start_requests() async start_requests()
Обработчики def parse(self, response) async def parse(self, response)
Переход по ссылкам response.follow(url) response.follow(url)
Выдача записей yield dict или yield Item yield dict
Планирование запросов Scheduler + Dupefilter Планировщик со встроенным удалением дубликатов
Загрузка Downloader + Middlewares Менеджер с поддержкой нескольких сессий
Обработка записей Item Pipelines Хук on_scraped_item()
Обнаружение блокировок Через свои middleware Встроенные хуки is_blocked() + retry_blocked_request()
Одновременные запросы Настройка CONCURRENT_REQUESTS Атрибут класса concurrent_requests
Ограничение доменов allowed_domains allowed_domains
Robots.txt Настройка ROBOTSTXT_OBEY Атрибут класса robots_txt_obey
Пауза и возобновление Настройка JOBDIR Аргумент конструктора crawldir
Экспорт Feed exports result.items.to_json() / to_jsonl() / to_csv() / to_xml() или свой экспорт через хуки
Запуск scrapy crawl spider_name MySpider().start()
Потоковая выдача Нет async for item in spider.stream()
Несколько сессий Нет Несколько сессий разных типов в одном пауке