Как быстро создать потрясающие наборы данных с помощью Scrapy в Python
Table Of Content
Scrapy - это настраиваемый и удобный для разработчика фреймворк для парсинга в Python. Он может помочь вам создать потрясающий парсер всего за несколько строк кода для сбора данных с веб-сайта. Лучшая часть заключается в том, что этот фреймворк обладает большой гибкостью. Его можно использовать в любой программе с помощью импорта, или вы можете создать проект-шаблон с помощью одной команды. Вы даже можете добавлять функциональность к своим проектам, добавляя любые библиотеки, такие как Beautiful Soup 4.
Цель сегодняшней статьи - получить подробную информацию о товарах с электронной коммерции со скоростью молнии. Для этого мы возьмем наш обычный пример веб-сайта для этого эксперимента. Я предполагаю, что вы уже установили Scrapy с помощью pip или conda. Если нет, просто воспользуйтесь одной из следующих команд.
pip install Scrapy# ИЛИconda install -c conda-forge scrapy
Настройка проекта
Давайте настроим наш проект, используя следующую команду.
scrapy startproject figurinesmaniac
После завершения Scrapy создаст для вас готовый к использованию проект краулера. Теперь нам осталось указать нашему краулеру, где найти данные. Лучший способ - все еще использовать инструменты разработчика Google для определения тегов, классов или идентификаторов, которые вам нужно искать. Если вы не знаете, как это сделать, я подробно объясняю процесс в этой статье о Selenium.
Создание паука
Мы ищем название, размеры и цену каждого продукта. Поэтому наш парсер должен просмотреть каждую страницу продукта и получить информацию с нее. Кроме того, есть несколько страниц. Нам нужно учесть пагинацию.
Для начала этой задачи нам нужно создать паука, которым будет пользоваться наш парсер. Этот паук будет содержать информацию о том, откуда получить нужную нам информацию и как управлять несколькими страницами и пагинацией. Внутри сгенерированного проекта Scrapy есть папка с названием spiders
. Именно здесь мы создадим наш products.py
, который будет являться нашим пауком. Вот пустой шаблон для базового паука. Я уже переименовал имя класса и атрибут name для нашего проекта.
import scrapyclass ProductsSpider(scrapy.Spider):
name = "products"
def start_requests(self):
urls = ["https://example.com"]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse) def parse(self, response):
print(response.url)
Первый вопрос - с какой страницы начнется наш парсер? Давайте сделаем все проще и начнем непосредственно с страницы всех продуктов. Установите ее внутри urls
следующим образом.
urls = ["[https://www.figurines-maniac.com/toutes-les-figurines/](https://www.figurines-maniac.com/toutes-les-figurines/)"]
Далее нам нужно получить ссылки на каждый видимый продукт, потому что есть еще несколько на других страницах.
product_links = response.xpath("//article/ul/li/a[1]")
Как только мы получили все местоположения продуктов, так как они являются тегами "a", мы можем сказать нашему парсеру перейти на каждый из них и получить данные со страницы. Но он не знает, что нужно извлечь со страницы. Нам нужно добавить функцию, которую он может использовать для этого.
def parse_product(self, response):
product_name = response.xpath("//h2[contains(@class, 'product_title')]/text()").get()
price = response.xpath("//bdi/text()").get()
dimensions = response.xpath("//table[contains(@class, 'shop_attributes')]//tr[contains(@class, 'dimensions')]/td/p/text()").get()
return {
"product_name": product_name,
"price": price,
"dimensions": dimensions
}
Я не вдаваюсь в подробности о том, как использовать XPath для получения данных из HTML-структуры, как указано выше, вы можете найти более подробную информацию об этом в моей статье о использовании Selenium в Python.
С этой добавленной функцией в нашем классе паука мы можем использовать ее, чтобы сказать нашему парсеру, что для каждой страницы продукта можно получить данные таким образом. И вот как мы реализуем это в нашей функции parse.
yield from response.follow_all(product_links, callback=self.parse_product)
Просто и легко, не так ли? Мы знаем, что нам нужно учесть пагинацию. Эта задача довольно тривиальна. Нам просто нужно добавить несколько строк в нашу функцию parse, чтобы перейти на следующую страницу. Есть несколько подходов, мой любимый - найти кнопку "следующая страница" и перейти на нее, потому что иногда номера пагинации могут ввести в заблуждение. В нашем случае мы проверяем, существует ли кнопка "следующая страница" и переходим на нее, используя рекурсию.
nav_next = response.xpath("//nav/ul/li/a[contains(@class, 'next')]").attrib["href"]
if nav_next is not None:
yield response.follow(nav_next, callback=self.parse)
Наслаждайтесь данными
Вот и все для нашего кода парсера. Последний шаг - запустить парсер с помощью командной строки и получить эти вкусные данные! В зависимости от выбранного расширения файла, Scrapy автоматически выводит данные в правильном формате файла. Здесь я выбрал файл CSV, но мы также можем заменить .csv
на .json
, чтобы получить файл JSON в выводе.
scrapy crawl products -O products.csv
Если вам понравилась статья или вы нашли ее полезной, будьте добры поддержать меня, следуя за мной здесь (Jonathan Mondaut). Скоро будут доступны еще больше статей! А пока вы можете найти полный код парсера ниже!
import scrapyclass ProductsSpider(scrapy.Spider):
name = "products"
def start_requests(self):
urls = ['https://www.figurines-maniac.com/toutes-les-figurines/']
for url in urls:
yield scrapy.Request(url=url, callback=self.parse) def parse_product(self, response):
product_name = response.xpath("//h2[contains(@class, 'product_title')]/text()").get()
price = response.xpath("//bdi/text()").get()
dimensions = response.xpath("//table[contains(@class, 'shop_attributes')]//tr[contains(@class, 'dimensions')]/td/p/text()").get()
return {
"product_name": product_name,
"price": price,
"dimensions": dimensions
} def parse(self, response):
product_links = response.xpath("//article/ul/li/a[1]")
if(len(product_links) > 0):
yield from response.follow_all(product_links, callback=self.parse_product)
nav_next = response.xpath("//nav/ul/li/a[contains(@class, 'next')]").attrib["href"]
if nav_next is not None:
yield response.follow(nav_next, callback=self.parse)