Узнайте, как использовать Sc ... Парсинг с помощью Scrapy
Парсинг с помощью Scrapy...
Узнайте, как использовать Scrapy в реальных проектах, используя его возможности и преодолевая его ограничения с помощью таких инструментов, как Playwright.
Введение
Что такое Scrapy?
Scrapy — это фреймворк для парсинга с открытым исходным кодом, написанный на языке Python, который предоставляет простой в использовании API для парсинга, а также встроенную функциональность для обработки крупномасштабных проектов парсинга, поддержку различных типов извлечения данных и возможность работы с различными веб-протоколами.
Почему стоит использовать Scrapy?
Scrapy является предпочтительным инструментом для крупномасштабных проектов парсинга благодаря своим преимуществам перед другими популярными библиотеками Python для веб-парсинга, такими как BeautifulSoup. BeautifulSoup — это в первую очередь библиотека парсеров, в то время как Scrapy — это полноценный фреймворк для парсинга с удобными встроенными функциями, такими как специальные типы спайдеров для различных задач парсинга и возможность расширения функциональности Scrapy за счет использования промежуточного программного обеспечения и экспорта данных в различные форматы.
Некоторые реальные примеры, где Scrapy может быть полезен, включают:
- Сайты электронной коммерции: Scrapy можно использовать для извлечения информации о товарах, такой как цены, описания и отзывы, с сайтов электронной коммерции, таких как Amazon, Walmart и Target.
- Социальные сети: Scrapy можно использовать для извлечения таких данных, как информация о пользователях и сообщения из популярных социальных сетей, таких как Twitter, Facebook и Instagram.
- Доски объявлений о работе: Scrapy можно использовать для мониторинга сайтов досок объявлений о работе, таких как Indeed, Glassdoor и LinkedIn, на предмет релевантных объявлений о работе.
Важно отметить, что Scrapy имеет некоторые ограничения. Например, он не может парсить сайты с большим количеством JavaScript. Однако мы можем легко преодолеть это ограничение, используя Scrapy вместе с другими инструментами, такими как Selenium или Playwright, для работы с этими сайтами. Итак, теперь, когда мы имеем представление о том, что такое Scrapy и почему он полезен, давайте углубимся в его основные возможности.
Изучение возможностей Scrapy
Типы спайдеров
Одной из ключевых особенностей Scrapy является возможность создавать различные типы спайдеров. Спайдеры, по сути, являются основой Scrapy и отвечают за разбор веб-сайтов и извлечение данных. В Scrapy существует три основных типа спайдеров:
- Spider: Базовый класс для всех спайдеров. Это самый простой тип спайдера, который используется для извлечения данных с одной страницы или небольшого набора страниц.
- CrawlSpider: Более продвинутый тип спайдера, который используется для извлечения данных с нескольких страниц или целых веб-сайтов. CrawlSpider автоматически переходит по ссылкам и извлекает данные с каждой страницы, которую посещает.
- SitemapSpider: Специализированный тип спайдера, который используется для извлечения данных с веб-сайтов, имеющих файл sitemap.xml. SitemapSpider автоматически посещает каждый URL-адрес в карте сайта и извлекает из него данные.
Ниже приведен пример создания базового спайдера в Scrapy:
import scrapy
class MySpider(scrapy.Spider):
name = "myspider"
start_urls = ["http://example.com"]
def parse(self, response): # extract data from response
Этот спайдер, названный myspider, начнет с запроса URL http://example.com. Метод parse — это то место, где вы напишете код для извлечения данных из ответа.
Вот пример того, как создать CrawlSpider в Scrapy:
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class MyCrawlSpider(CrawlSpider):
name = "mycrawlspider"
start_urls = ["http://example.com"]
rules = [ Rule(LinkExtractor(), callback='parse_item', follow=True) ] def parse_item(self, response): # extract data from response
Этот спайдер, названный mycrawlspider, начнет работу с запроса URL http://example.com. Список правил содержит один объект Rule, который указывает спайдеру перейти по всем ссылкам и вызвать метод parse_item для каждого ответа.
Расширение Scrapy с помощью Middlewares
Middlewares позволяют нам расширить функциональность Scrapy. Scrapy поставляется с несколькими встроенными промежуточными модулями, которые можно использовать из коробки. Кроме того, мы можем написать собственное промежуточное ПО для выполнения таких задач, как изменение заголовков запросов, логирование или обработка исключений. Итак, давайте рассмотрим некоторые из наиболее часто используемых промежуточных программ Scrapy:
- UserAgentMiddleware: Это промежуточное ПО позволяет вам устанавливать пользовательский заголовок User-Agent для каждого запроса. Это полезно для того, чтобы избежать обнаружения сайтами, которые могут блокировать ботов-парсеров на основе заголовка User-Agent. Чтобы использовать это промежуточное ПО, мы можем установить его в файле настроек Scrapy следующим образом:
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'scrapy.downloadermiddlewares.useragent': 500,
}
В этом примере мы используем приоритет 500 для встроенной программы UserAgentMiddleware, чтобы обеспечить ее запуск перед другими программами-загрузчиками. По умолчанию UserAgentMiddleware устанавливает в заголовке User-Agent для каждого запроса случайно выбранную строку агента пользователя. Вы можете настроить используемые строки агента пользователя, задав параметр USER_AGENT в настройках Scrapy.
Обратите внимание, что мы сначала установили UserAgentMiddleware на None, прежде чем добавить его к параметру DOWNLOADER_MIDDLEWARES с другим приоритетом. Это связано с тем, что по умолчанию UserAgentMiddleware в Scrapy устанавливает общую строку агента пользователя для всех запросов, что может быть не идеальным для некоторых сценариев парсинга. Если нам нужно использовать пользовательскую строку агента пользователя, нам нужно будет настроить UserAgentMiddleware.
Поэтому, установив для UserAgentMiddleware значение None, мы указываем Scrapy удалить UserAgentMiddleware по умолчанию из параметра DOWNLOADER_MIDDLEWARES, прежде чем добавлять наш собственный экземпляр промежуточного ПО с другим приоритетом.
- RetryMiddleware: Scrapy поставляется с RetryMiddleware, которое можно использовать для повторного выполнения неудачных запросов. По умолчанию она повторяет запросы с кодами состояния HTTP 500, 502, 503, 504, 408, а также когда возникает исключение. Вы можете настроить поведение этого промежуточного ПО, указав параметры RETRY_TIMES и RETRY_HTTP_CODES. Чтобы использовать это промежуточное ПО в конфигурации по умолчанию, вы можете просто добавить его в настройки Scrapy:
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,
}
- HttpProxyMiddleware: Это промежуточное ПО позволяет вам использовать прокси для отправки запросов. Это полезно для избежания обнаружения и обхода ограничений скорости IP. Чтобы использовать это промежуточное ПО, мы можем добавить его в файл настроек Scrapy следующим образом:
DOWNLOADER_MIDDLEWARES = { 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110, 'myproject.middlewares.ProxyMiddleware': 100, } PROXY_POOL_ENABLED = True
Это включит HttpProxyMiddleware, а также включит ProxyMiddleware, который мы определим. Это промежуточное ПО будет выбирать случайный прокси для каждого запроса из пула прокси, предоставленного пользователем.
- CookiesMiddleware: Это промежуточное ПО позволяет обрабатывать файлы cookie, отправляемые веб-сайтами. По умолчанию Scrapy хранит куки в памяти, но вы также можете хранить их в файле или базе данных, указав COOKIES_STORAGE в настройках Scrapy. Чтобы добавить **CookiesMiddleware** к настройке **DOWNLOADER_MIDDLEWARES**, мы просто указываем класс промежуточного ПО и его приоритет. В данном случае мы используем приоритет 700, который должен быть после стандартных UserAgentMiddleware и **RetryMiddleware**, но перед любым пользовательским промежуточным ПО.
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware': 700,
}
Теперь мы можем использовать CookiesMiddleware для обработки файлов cookie, отправленных веб-сайтом:
from scrapy import Spider, Request class MySpider(Spider): name = 'myspider' start_urls = ['<https://www.example.com/>'] def start_requests(self): for url in self.start_urls: # Send an initial request without cookies yield Request(url, cookies={}, callback=self.parse) def parse(self, response): # Extract cookies from the response headers cookies = {} for cookie in response.headers.getlist('Set-Cookie'): key, value = cookie.decode('utf-8').split('=', 1) cookies[key] = value.split(';')[0] # Send a new request with the cookies received yield Request('<https://www.example.com/protected>', cookies=cookies, callback=self.parse_protected) def parse_protected(self, response): # Process the protected page here pass
Когда спайдер отправляет первоначальный запрос на https://www.example.com/, мы еще не отправляем никаких файлов cookie. Когда мы получаем ответ, мы извлекаем куки из заголовков ответа и отправляем новый запрос на защищенную страницу с полученными куки. Это лишь несколько вариантов использования промежуточных модулей в Scrapy. Прелесть промежуточных модулей заключается в том, что мы можем писать свои собственные промежуточные модули, чтобы продолжать расширять возможности Scrapy и выполнять дополнительные задачи для наших конкретных случаев использования.
Экспорт собранных данных
Scrapy предоставляет встроенную поддержку для экспорта собранных данных в различные форматы, такие как CSV, JSON и XML. Вы также можете создавать свои собственные экспортеры для хранения данных в различных форматах.
Вот пример того, как сохранить спарсенные данные в CSV-файл в Scrapy:
Обратите внимание, что это очень простой пример, и метод closed может быть модифицирован для обработки ошибок и обеспечения правильного закрытия файла. Кроме того, код является просто пояснением, и вам придется адаптировать его, чтобы он работал в вашем случае.
import scrapy from scrapy.exporters import CsvItemExporter class MySpider(scrapy.Spider): name = 'example' start_urls = ['<https://www.example.com>'] def parse(self, response): items = response.xpath('//div[@class="item"]') for item in items: yield { 'title': item.xpath('.//h2/text()').get(), 'description': item.xpath('.//p/text()').get(), } def closed(self, reason): filename = "example.csv" with open(filename, 'w+b') as f: exporter = CsvItemExporter(f) exporter.fields_to_export = ['title', 'description'] exporter.export_item(item for item in self.parse())
В этом примере мы определяем спайдера, который начинает работу с поиска URL-адреса «https://www.example.com». URL. Затем мы определяем метод разбора, который извлекает название, цену и описание для каждого товара на странице. Наконец, в закрытом методе мы определяем имя файла CSV и экспортируем извлеченные данные с помощью CsvItemExporter. Другой способ экспорта извлеченных данных в различные форматы с помощью Scrapy — использовать команду scrapy crawl и указать желаемый формат файла нашего вывода. Это можно сделать, добавив флаг -o, за которым следует имя файла и расширение выходного файла.
Например, если мы хотим вывести собранные данные в формате JSON, мы используем следующую команду:
scrapy crawl myspider -o output.json
Это сохранит собранные данные в файле с именем output.json в том же каталоге, где была выполнена команда. Аналогично, если мы хотим вывести данные в формате CSV, мы используем следующую команду:
scrapy crawl myspider -o output.csv
Это сохранит собранные данные в файле с именем output.csv в том же каталоге, где была выполнена команда. В целом, Scrapy предоставляет несколько способов хранения и экспорта отсканированных данных, давая нам возможность выбрать наиболее подходящий метод для нашей конкретной ситуации. Теперь, когда мы лучше понимаем, что возможно с помощью Scrapy, давайте рассмотрим, как мы можем использовать этот фреймворк для извлечения данных с реальных веб-сайтов. Для этого мы создадим несколько небольших проектов, каждый из которых продемонстрирует различные возможности Scrapy.
Проект: Создание программы парсера для новостей Хакера с использованием базового спайдера
В этом разделе мы узнаем, как настроить проект Scrapy и создать базового спайдера для поиска заголовка, автора, URL и пунктов всех статей, отображаемых на первой странице сайта Hacker News.
Создание проекта Scrapy
Прежде чем мы сможем создать спайдер, нам нужно создать новый проект Scrapy. Для этого мы воспользуемся терминалом. Откройте окно терминала и перейдите в каталог, где вы хотите создать проект. Начните с установки Scrapy:
pip install scrapy
Затем выполните следующую команду:
scrapy startproject hackernews
Эта команда создаст новый каталог под названием «hackernews» с базовой структурой проекта Scrapy.
Создание спайдера
Теперь, когда у нас есть проект Scrapy, мы можем создать спайдера для поиска нужных нам данных. В том же окне терминала перейдите в каталог проекта с помощью команды cd hackernews и выполните следующую команду:
scrapy genspider hackernews_spider news.ycombinator.com
Эта команда создаст нового спайдера в каталоге spiders нашего проекта. Мы назвали спайдера hackernews_spider и установили начальный URL на news.ycombinator.com, который является нашим целевым сайтом.
Написание кода спайдера
Далее откроем файл hackernews_spider.py в каталоге spiders нашего проекта. Мы увидим базовый шаблон для Scrapy-спайдера.
import scrapy class HackernewsSpiderSpider(scrapy.Spider): name = 'hackernews_spider' allowed_domains = ['news.ycombinator.com'] start_urls = ['http://news.ycombinator.com/'] def parse(self, response): pass
Прежде чем двигаться дальше, давайте быстро разберем, что мы видим:
- Атрибут name — это имя спайдера.
- Атрибут allowed_domains — это список доменов, которые спайдеру разрешено парсить.
- Атрибут start_urls — это список URL, с которых спайдер должен начать поиск.
- Метод parse — это метод, который Scrapy вызывает для обработки ответа от каждого URL из списка start_urls.
Отлично, теперь самое интересное. Давайте добавим немного кода в метод parse, чтобы получить нужные нам данные.
import scrapy class HackernewsSpiderSpider(scrapy.Spider): name = 'hackernews_spider' allowed_domains = ['news.ycombinator.com'] start_urls = ['http://news.ycombinator.com/'] def parse(self, response): articles = response.css('tr.athing') for article in articles: yield { "URL": article.css(".titleline a::attr(href)").get(), "title": article.css(".titleline a::text").get(), "rank": article.css(".rank::text").get().replace(".", "") }
В этом коде мы используем метод css для извлечения данных из ответа. Мы выбираем все статьи на странице с помощью CSS-селектора tr.athing, а затем извлекаем заголовок, URL и рейтинг для каждой статьи с помощью более конкретных селекторов. Наконец, мы используем ключевое слово yield, чтобы вернуть словарь Python с собранными данными.
Запуск Hacker News Spider
Теперь, когда наш спайдер готов, давайте запустим его и посмотрим, как он работает. По умолчанию данные выводятся на консоль, но мы также можем экспортировать их в другие форматы, такие как JSON, CSV или XML, указав формат вывода при запуске парсера. Чтобы продемонстрировать это, давайте запустим наш спайдер и экспортируем извлеченные данные в файл JSON:
scrapy crawl hackernews -o hackernews.json
Это сохранит данные в файл с именем **hackernews.json** в корневом каталоге проекта. Вы можете использовать ту же команду для экспорта данных в другие форматы, заменив расширение файла на нужный формат (например, o hackernews.csv для формата CSV). На этом запуск спайдера закончен. В следующем разделе мы рассмотрим, как можно использовать CrawlSpider в Scrapy для извлечения данных со всех страниц сайта Hacker News.
Проект: Создание парсера для Hacker News с использованием CrawlSpider
В предыдущем разделе было показано, как парсить данные с одной страницы с помощью базового спайдера. Хотя можно написать код для постраничного просмотра остальных страниц и спарсить все статьи на HN с помощью базового Spider, Scrapy предлагает нам лучшее решение: CrawlSpider. Итак, без лишних слов, давайте перейдем непосредственно к коду.
Настройка проекта
Для начала создадим новый проект Scrapy под названием hackernews_crawlspider с помощью следующей команды в терминале:
scrapy startproject hackernews_crawlspider
Далее давайте создадим нового спайдера, используя шаблон CrawlSpider. CrawlSpider является подклассом класса Spider и предназначен для рекурсивного перехода по ссылкам и сбора данных с нескольких страниц.
scrapy genspider -t crawl hackernews_spider https://news.ycombinator.com/
Эта команда создает нового спайдера под названием «hackernews_spider» в каталоге «spiders» вашего проекта Scrapy. Она также указывает, что спайдер должен использовать шаблон CrawlSpider и начать с поиска главной страницы Hacker News.
Код
Наша цель в этом парсере — извлечь из каждой статьи те же данные, которые мы извлекли в предыдущем разделе: URL, заголовок и рейтинг. Разница в том, что теперь мы определим набор правил для парсера, которым он будет следовать при просмотре сайта. Например, мы определим правило, указывающее парсеру, где он может найти правильные ссылки для постраничного просмотра содержимого HN.
Вот как будет выглядеть окончательный вариант кода для нашего сценария использования:
# Add imports CrawlSpider, Rule and LinkExtractor from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor # Change the spider from "scrapy.Spider" to "CrawlSpider" class HackernewsSpider(CrawlSpider): name = 'hackernews' allowed_domains = ['news.ycombinator.com'] start_urls = ['<https://news.ycombinator.com/news>'] custom_settings = { 'DOWNLOAD_DELAY': 1 # Add a 1-second delay between requests } # Define a rule that should be followed by the link extractor. # In this case, Scrapy will follow all the links with the "morelink" class # And call the "parse_article" function on every crawled page rules = ( Rule(LinkExtractor(allow=[r'news\.ycombinator\.com/news$']), callback='parse_article'), Rule(LinkExtractor(restrict_css='.morelink'), callback='parse_article', follow=True), ) # When using the CrawlSpider we cannot use a parse function called "parse". # Otherwise, it will override the default function. # So, just rename it to something else, for example, "parse_article" def parse_article(self, response): for article in response.css('tr.athing'): yield { "URL": article.css(".titleline a::attr(href)").get(), "title": article.css(".titleline a::text").get(), "rank": article.css(".rank::text").get().replace(".", "") }
Теперь давайте разберем код, чтобы понять, что CrawlSpider делает для нас в этом сценарии. Вы можете заметить, что некоторые части этого кода уже были сгенерированы CrawlSpider, в то время как другие части очень похожи на то, что мы делали при написании базового Spider. Первая отличительная часть кода, которая может привлечь ваше внимание, — это включенный нами атрибут custom_settings. Он добавляет 1-секундную задержку между запросами. Поскольку сейчас мы отправляем несколько запросов для доступа к различным страницам сайта, дополнительная задержка между запросами может быть полезна для предотвращения перегрузки целевого сайта слишком большим количеством запросов одновременно.
Далее мы определили набор правил, которым нужно следовать при просмотре сайта, используя атрибут rules:
rules = ( Rule(LinkExtractor(allow=[r'news\.ycombinator\.com/news$']), callback='parse_article'), Rule(LinkExtractor(restrict_css='.morelink'), callback='parse_article', follow=True), )
Каждое правило определяется с помощью класса Rule, который принимает два аргумента: экземпляр LinkExtractor, определяющий, по каким ссылкам нужно следовать, и функцию обратного вызова, которая будет вызываться для обработки ответа от каждой просмотренной страницы. В данном случае у нас есть два правила:
- Первое правило использует экземпляр LinkExtractor с параметром allow, который соответствует URL-адресам, заканчивающимся на «news.ycombinator.com/news». Это будет соответствовать первой странице новостных статей на Hacker News. Мы задаем параметр обратного вызова parse_article — это функция, которая будет вызываться для обработки ответа от каждой страницы, соответствующей этому правилу.
- Второе правило использует экземпляр LinkExtractor с параметром restrict_css, который соответствует классу morelink. Это будет соответствовать ссылке «Еще» в нижней части каждой страницы новостных статей на Hacker News. Мы снова задали параметр обратного вызова parse_article, а параметр follow — True, что говорит Scrapy переходить по ссылкам на этой странице, которые соответствуют заданному селектору.
Наконец, мы определили функцию parse_article, которая принимает в качестве аргумента объект ответа. Эта функция вызывается для обработки ответа от каждой страницы, которая соответствует одному из правил, определенных в атрибуте rules.
def parse_article(self, response): for article in response.css('tr.athing'): yield { "URL": article.css(".titleline a::attr(href)").get(), "title": article.css(".titleline a::text").get(), "rank": article.css(".rank::text").get().replace(".", "") }
В этой функции мы используем метод response.css для извлечения данных из HTML страницы. В частности, мы ищем все элементы tr с классом athing и извлекаем URL, заголовок и рейтинг каждой статьи. Затем мы используем ключевое слово yield, чтобы вернуть словарь Python с этими данными. Помните, что ключевое слово yield используется вместо return, потому что Scrapy обрабатывает ответ асинхронно, и функция может быть вызвана несколько раз. Также стоит отметить, что мы назвали функцию parse_article вместо стандартной функции parse, которая используется в Scrapy Spiders.
Это связано с тем, что когда вы используете класс CrawlSpider, функция разбора по умолчанию используется для разбора ответа от первой просмотренной страницы. Если вы определите собственную функцию разбора в CrawlSpider, она переопределит функцию по умолчанию, и ваш спайдер не будет работать так, как ожидалось. Чтобы избежать этой проблемы, считается хорошей практикой всегда называть наши пользовательские функции разбора как-то иначе, чем parse. В данном случае мы назвали нашу функцию parse_article, но вы можете выбрать любое другое имя, подходящее для вашего спайдера.
Запуск CrawlSpider
Отлично, теперь, когда мы понимаем, что происходит в нашем коде, пришло время испытать нашего спайдера на практике, запустив его с помощью следующей команды:
scrapy crawl hackernews -o hackernews.json
Это позволит запустить спайдера и спарсить данные со всех новостей на всех страницах сайта Hacker News. Мы также уже воспользовались возможностью указать Scrapy выводить все спарсенные данные в JSON-файл, что облегчит нам визуализацию полученных результатов.
Как парсить сайты, перегруженные JavaScript.
Парсинг веб-сайтов, содержащих JavaScript, может оказаться сложной задачей, поскольку Scrapy в первую очередь предназначен для парсинга статических HTML-страниц. Однако мы можем обойти это ограничение, используя безголовый браузер, такой как Playwright, в сочетании со Scrapy для парсинга динамических веб-страниц. Playwright — это библиотека, которая предоставляет высокоуровневый API для управления безголовыми браузерами Chrome, Firefox и Safari. Используя Playwright, мы можем программно взаимодействовать с целевой веб-страницей для имитации действий пользователя и извлечения данных из динамически загружаемых элементов.
Чтобы использовать Playwright в Scrapy, нам необходимо создать пользовательское промежуточное ПО, которое инициализирует экземпляр браузера Playwright и получает HTML-содержимое веб-страницы с помощью Playwright. Затем промежуточное ПО может передать HTML-содержимое в Scrapy для разбора и извлечения данных. К счастью, библиотека scrapy-playwright позволяет нам легко интегрировать Playwright со Scrapy. В следующем разделе мы построим небольшой проект с использованием этой комбинации для извлечения данных с сайта Mint Mobile, перегруженного JavaScript. Но прежде чем двигаться дальше, давайте вкратце рассмотрим целевую веб-страницу и поймем, почему мы не сможем извлечь нужные нам данные с помощью одной только Scrapy.
Mint Mobile требует JavaScript для загрузки значительной части содержимого, отображаемого на странице продукта, что делает его идеальным сценарием для использования Playwright в контексте парсинга:
Страница продукта Mint Mobile с отключенным JavaScript:
Страница продукта Mint Mobile с включенным JavaScript:
Как видите, без включенного JavaScript мы потеряем значительную часть данных, которые хотим извлечь. Поскольку Scrapy не может загружать JavaScript, вы можете считать первое изображение с отключенным JavaScript «представлением Scrapy», а второе изображение с включенным JavaScript — «представлением Playwright». Отлично, теперь, когда мы знаем, почему нам нужна библиотека автоматизации браузера, такая как Playwright, чтобы спарсить эту страницу, пришло время воплотить эти знания в код, создав наш следующий проект: парсер Mint Mobile.
Проект: Создание парсера с помощью Scrapy и Playwright
Настройка проекта
Мы начнем с создания каталога для размещения нашего проекта и установки необходимых зависимостей:
# Create new directory and move into it mkdir scrapy-playwright cd scrapy-playwright
Установка:
# Install Scrapy and scrapy-playwright pip install scrapy scrapy-playwright # Install the required browsers if you are running Playwright for the first time playwright install
Далее мы запускаем проект Scrapy и генерируем спайдер:
scrapy startproject scrapy_playwright_project scrapy genspider mintmobile https://www.mintmobile.com/
Теперь давайте активируем scrapy-playwright, добавив несколько строк конфигурации в наше промежуточное ПО DOWNLOAD_HANDLERS.
# scrapy-playwright configuration DOWNLOAD_HANDLERS = { "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler", "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler", }
Отлично! Теперь мы готовы написать код для поиска нашего целевого сайта.
Код
import scrapy from scrapy_playwright.page import PageMethod class MintmobileSpider(scrapy.Spider): name = 'mintmobile' def start_requests(self): yield scrapy.Request('<https://www.mintmobile.com/product/google-pixel-7-pro-bundle/>', meta= dict( # Use Playwright playwright = True, # Keep the page object so we can work with it later on playwright_include_page = True, # Use PageMethods to wait for the content we want to scrape to be properly loaded before extracting the data playwright_page_methods = [ PageMethod('wait_for_selector', 'div.m-productCard--device') ] )) def parse(self, response): yield { "name": response.css("div.m-productCard__heading h1::text").get().strip(), "memory": response.css("div.composited_product_details_wrapper > div > div > div:nth-child(2) > div.label > span::text").get().replace(':', '').strip(), "pay_monthly_price": response.css("div.composite_price_monthly > span::text").get(), "pay_today_price": response.css("div.composite_price p.price span.amount::attr(aria-label)").get().split()[0], };
В методе start_requests спайдер делает один HTTP-запрос к странице продукта мобильного телефона на сайте Mint Mobile. Мы инициализируем этот запрос с помощью класса scrapy.Request, передавая мета-словарь, задающий опции, которые мы хотим использовать для Playwright при сканировании страницы. Эти параметры включают playwright, установленный в True, чтобы указать, что Playwright должен быть использован, затем playwright_include_page также установлен в True, чтобы мы могли сохранить объект страницы, чтобы его можно было использовать позже, и playwright_page_methods, установленный в список объектов PageMethod.
В данном случае имеется только один объект PageMethod, который использует метод Playwright wait_for_selector для ожидания появления на странице определенного CSS-селектора. Это делается для того, чтобы убедиться, что страница правильно загрузилась, прежде чем мы начнем извлекать ее данные. В методе разбора спайдер использует селекторы CSS для извлечения данных со страницы. Извлекаются четыре части данных: название продукта, объем его памяти, цена_за_месяц, а также цена_за_день.
Ожидаемый результат:
Наконец, давайте запустим нашего спайдера с помощью команды scrapy crawl mintmobile -o data.json, чтобы извлечь целевые данные и сохранить их в файле data.json:
[ { "name": "Google Pixel 7 Pro", "memory": "128GB", "pay_monthly_price": "50", "pay_today_price": "589" } ]
Развертывание спайдеров Scrapy в облаке
Далее мы узнаем, как развернуть спайдеров Scrapy в облаке с помощью Apify. Это позволит нам настроить их запуск по расписанию и получить доступ ко многим другим возможностям платформы. Чтобы продемонстрировать это, мы воспользуемся Apify SDK для Python и выберем шаблон разработки Scrapy, который поможет нам запустить процесс настройки. Затем мы изменим сгенерированный код шаблона для запуска нашего парсера CrawlSpider Hacker News. Давайте приступим.
Установка Apify CLI
Чтобы начать работу с Apify CLI, нам нужно сначала установить его. Это можно сделать двумя способами: через менеджер пакетов Homebrew на macOS или Linux или через менеджер пакетов Node.js (NPM).
Через homebrew
На macOS (или Linux) вы можете установить Apify CLI через менеджер пакетов Homebrew.
brew install apify/tap/apify-cli
Через NPM
Установите или обновите Apify CLI, выполнив команду:
npm -g install apify-cli
Создание нового актера
После установки Apify CLI на ваш компьютер просто выполните следующую команду в терминале:
apify create scrapy-actor
Затем выберите Python → Scrapy → Установить шаблон
Эта команда создаст новую папку с именем scrapy-actor, установит все необходимые зависимости и создаст шаблонный код, который мы можем использовать для начала разработки с использованием Scrapy и Apify SDK для Python. Наконец, перейдите в только что созданную папку и откройте ее с помощью предпочитаемого редактора кода, в данном примере я использую VS Code.
cd scrapy-actor code .
Настройка шаблона Scrapy Actor
Шаблон уже создает полностью функциональный парсинг. Вы можете запустить его с помощью команды apify run. Если вы хотите попробовать его до того, как мы изменим код, результаты парсинга будут храниться в разделе storage/datasets. Теперь, когда мы знакомы с шаблоном, мы можем изменить его, чтобы приспособить к нашему парсеру HackerNews.
Чтобы внести первую поправку, нам нужно заменить код шаблона в src/spiders/title_spider.py на наш собственный код. После замены ваш код должен выглядеть следующим образом:
# Add imports CrawlSpider, Rule and LinkExtractor from scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor # Change the spider from "scrapy.Spider" to "CrawlSpider" class HackernewsSpider(CrawlSpider): name = 'hackernews' allowed_domains = ['news.ycombinator.com'] start_urls = ['<https://news.ycombinator.com/news>'] custom_settings = { 'DOWNLOAD_DELAY': 1 # Add a 1-second delay between requests } # Define a rule that should be followed by the link extractor. # In this case, Scrapy will follow all the links with the "morelink" class # And call the "parse_article" function on every crawled page rules = ( Rule(LinkExtractor(allow=[r'news\.ycombinator\.com/news$']), callback='parse_article'), Rule(LinkExtractor(restrict_css='.morelink'), callback='parse_article', follow=True), ) # When using the CrawlSpider we cannot use a parse function called "parse". # Otherwise, it will override the default function. # So, just rename it to something else, for example, "parse_article" def parse_article(self, response): for article in response.css('tr.athing'): yield { "URL": article.css(".titleline a::attr(href)").get(), "title": article.css(".titleline a::text").get(), "rank": article.css(".rank::text").get().replace(".", "") }
Наконец, перед запуском Actor нам необходимо внести некоторые изменения в файл main.py, чтобы привести его в соответствие с теми изменениями, которые мы внесли в исходный шаблон спайдера.
from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings from apify import Actor from .pipelines import ActorDatasetPushPipeline from .spiders.hackernews_spider import HackernewsSpider async def main(): async with Actor: actor_input = await Actor.get_input() or {} max_depth = actor_input.get('max_depth', 1) start_urls = [start_url.get('url') for start_url in actor_input.get('start_urls', [{ 'url': '<https://news.ycombinator.com/news>' }])] settings = get_project_settings() settings['ITEM_PIPELINES'] = { ActorDatasetPushPipeline: 1 } settings['DEPTH_LIMIT'] = max_depth process = CrawlerProcess(settings, install_root_handler=False) # If you want to run multiple spiders, call `process.crawl` for each of them here process.crawl(HackernewsSpider, start_urls=start_urls) process.start()
Запуск актера локально
Отлично! Теперь мы готовы запустить наш актор Scrapy. Для этого давайте введем в терминале команду apify run. Через несколько секунд хранилище/datasets будет заполнено данными, взятыми из Hacker News.
Развертывание актора в Apify
Перед развертыванием Actor в Apify нам нужно сделать последнюю настройку. Перейдите в .actor/input_schema.json и измените URL prefill на https://news.ycombinator.com/news. Это изменение важно при запуске парсера на платформе Apify.
Теперь, когда мы знаем, что наш Actor работает так, как ожидалось, пришло время развернуть его на платформе Apify. Для этого вам необходимо зарегистрировать бесплатную учетную запись Apify. Как только вы зарегистрируете учетную запись Apify, выполните команду apify login в терминале. Вам будет предложено ввести токен API Apify. Его можно найти в Apify Console в разделе Настройки → Интеграции. Последним шагом будет выполнение команды apify push. Это запустит сборку Актора, и через несколько секунд вы сможете увидеть только что созданного Актора в Apify Console в разделе Actors → My actors.
Отлично! Ваш парсер готов к работе на платформе Apify. Чтобы начать работу, нажмите кнопку Start. После завершения работы вы можете просмотреть и загрузить свои данные в различных форматах на вкладке Storage.