Начало работы¶
Введение¶
Что нужно знать
- Прочитайте страницу Основы Fetcher, чтобы узнать о разных типах Fetcher и понять, когда их использовать.
- Прочитайте страницу Основные классы, чтобы познакомиться с классами Selector и Response.
- Прочитайте страницу Архитектура, чтобы понять, как устроена система пауков.
Система пауков позволяет создать краулер, который обходит много страниц и выполняет запросы одновременно, всего в нескольких строках кода. Если вы работали со Scrapy, многие приёмы будут знакомы. Если нет, это руководство поможет начать.
Ваш первый паук¶
Паук представляет собой класс, который определяет, как обходить сайты и извлекать данные. Вот самый простой пример:
from scrapling.spiders import Spider, Response
class QuotesSpider(Spider):
name = "quotes"
start_urls = ["https://quotes.toscrape.com"]
async def parse(self, response: Response):
for quote in response.css("div.quote"):
yield {
"text": quote.css("span.text::text").get(""),
"author": quote.css("small.author::text").get(""),
}
Каждому пауку нужны три элемента:
name- уникальное имя паука.start_urls- список URL, с которых начинается обход.parse()- метод с асинхронным генератором, который обрабатывает каждый ответ и выдаёт результаты.
Основная работа происходит в методе parse(). В нём доступны те же методы выбора элементов, что и в Selector/Response. Чтобы выдать извлечённые записи, используйте yield со словарями.
Запуск паука¶
Создайте экземпляр паука и вызовите start():
Метод start() сам управляет асинхронным выполнением, поэтому настраивать цикл событий не нужно. Во время работы все события записываются в терминал. В конце обхода вы получите подробную статистику.
Она находится в возвращённом объекте CrawlResult, вместе с результатами обхода:
result = QuotesSpider().start()
# Access scraped items
for item in result.items:
print(item["text"], "-", item["author"])
# Check statistics
print(f"Scraped {result.stats.items_scraped} items")
print(f"Made {result.stats.requests_count} requests")
print(f"Took {result.stats.elapsed_seconds:.1f} seconds")
# Did the crawl finish or was it paused?
print(f"Completed: {result.completed}")
Переход по ссылкам¶
При обходе обычно нужно переходить по ссылкам между страницами. Создавайте следующие запросы с помощью response.follow():
from scrapling.spiders import Spider, Response
class QuotesSpider(Spider):
name = "quotes"
start_urls = ["https://quotes.toscrape.com"]
async def parse(self, response: Response):
# Extract items from the current page
for quote in response.css("div.quote"):
yield {
"text": quote.css("span.text::text").get(""),
"author": quote.css("small.author::text").get(""),
}
# Follow the "next page" link
next_page = response.css("li.next a::attr(href)").get()
if next_page:
yield response.follow(next_page, callback=self.parse)
response.follow() автоматически объединяет относительные URL с URL текущей страницы. По умолчанию он также указывает текущую страницу в заголовке Referer.
Для разных типов страниц можно назначить разные методы-обработчики:
async def parse(self, response: Response):
for link in response.css("a.product-link::attr(href)").getall():
yield response.follow(link, callback=self.parse_product)
async def parse_product(self, response: Response):
yield {
"name": response.css("h1::text").get(""),
"price": response.css(".price::text").get(""),
}
Note
Все обработчики должны быть асинхронными генераторами с async def и yield.
Экспорт данных¶
Объект ItemList в result.items содержит встроенные методы экспорта:
result = QuotesSpider().start()
# Export as JSON
result.items.to_json("quotes.json")
# Export as JSON with pretty-printing
result.items.to_json("quotes.json", indent=True)
# Export as JSON Lines (one JSON object per line)
result.items.to_jsonl("quotes.jsonl")
# Export as CSV or XML
result.items.to_csv("quotes.csv")
result.items.to_xml("quotes.xml")
Все эти методы автоматически создают родительские каталоги, если их ещё нет.
to_csv() создаёт столбец для каждого ключа, найденного в записях. Поэтому записи с разными наборами ключей тоже попадают в файл, а ячейки без значений остаются пустыми. Передайте fields=[...], чтобы выбрать столбцы и их порядок, или delimiter="\t", чтобы получить TSV-файл. to_xml() помещает каждую запись в элемент <item> внутри корневого элемента <items>. Их имена можно изменить через root_tag и item_tag.
В обоих форматах сложные значения, такие как вложенный словарь или список, записываются как JSON. Данные не отбрасываются без уведомления.
Ограничение доменов¶
Используйте allowed_domains, чтобы ограничить обход заданными доменами. Это не даст пауку случайно перейти на сторонний сайт:
class MySpider(Spider):
name = "my_spider"
start_urls = ["https://example.com"]
allowed_domains = {"example.com"}
async def parse(self, response: Response):
for link in response.css("a::attr(href)").getall():
# Links to other domains are silently dropped
yield response.follow(link, callback=self.parse)
Поддомены разрешаются автоматически. Например, allowed_domains = {"example.com"} также разрешает sub.example.com, blog.example.com и другие поддомены.
Отброшенные запросы учитываются в stats.offsite_requests_count, где можно посмотреть их число.
Соблюдение robots.txt¶
Установите robots_txt_obey = True, чтобы паук проверял правила robots.txt перед обходом каждого домена:
class PoliteSpider(Spider):
name = "polite"
start_urls = ["https://example.com"]
robots_txt_obey = True
async def parse(self, response: Response):
for link in response.css("a::attr(href)").getall():
yield response.follow(link, callback=self.parse)
При включённой настройке паук:
- Заранее загружает robots.txt для всех доменов из
start_urlsперед началом обхода. Загрузка идёт одновременно. - Проверяет каждый запрос по правилам
Disallowв robots.txt домена. Запрещённые запросы отбрасываются без уведомления и учитываются вstats.robots_disallowed_count. - Соблюдает директивы
Crawl-delayиRequest-rate, выбирая наибольшее значение между задержкой из директивы и настройкойdownload_delay. Поэтому правила robots.txt могут только увеличить заданную задержку. Если включён AutoThrottle, его максимальная задержка также ограничивает это значение. Задайте достаточно большое значениеautothrottle_max_delay, чтобы соблюдать требуемую задержку.
Файлы robots.txt загружаются через сессию паука по умолчанию и хранятся в кеше для каждого домена до конца обхода. Для доменов, найденных во время обхода и отсутствующих в start_urls, robots.txt загружается при первом запросе к домену.
Примечание: robots_txt_obey по умолчанию выключен, чтобы не менять поведение неожиданно. Если его включить, настройки числа одновременных запросов (concurrent_requests, concurrent_requests_per_domain) не изменятся. Меняется только задержка между запросами.
Если вы не хотите выбирать download_delay вручную, установите autothrottle_enabled = True. Паук сам подберёт задержку для каждого домена по скорости его ответов и увеличит её при блокировке. Подробнее в разделе AutoThrottle.
Что дальше¶
Теперь можно перейти к следующим темам:
- Запросы и ответы: приоритет запросов, удаление дубликатов, метаданные и другие возможности.
- Сессии: разные типы Fetcher (HTTP, браузер, stealth) в одном пауке.
- Прокси и блокировки: смена прокси между запросами и обработка блокировок.
- Расширенные возможности: число одновременных запросов, AutoThrottle, пауза и возобновление, потоковая выдача, хуки жизненного цикла и журналирование.