CoderCastrov logo
CoderCastrov
Парсер

Как быстро создать потрясающие наборы данных с помощью Scrapy в Python

Как быстро создать потрясающие наборы данных с помощью Scrapy в Python
просмотров
4 мин чтение
#Парсер

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)