Веб-скрапинг с Scrapy на Python

—

от автора

Привет, Хабр! Сегодня мы поговорим о хорошем инструменте для веб-скрапинга, который зарекомендовал себя, библиотеке Scrapy для Python.

Установим:

pip install Scrapy

Начнем

Scrapy использует спец. классы, называемые пауками, в коде — spiders, для сбора информации с веб-страниц:

import scrapy  class ExampleSpider(scrapy.Spider):     name = 'otus'     allowed_domains = ['example.com']     start_urls = ['http://example.com/']      def parse(self, response):         title = response.css('title::text').get()         yield {'title': title}
  • name — имя паучка.

  • allowed_domains — список доменов, с которых паук может собирать данные.

  • start_urls — список URL-адресов, с которых паук начинает сбор данных.

  • parse вызывается для каждого ответа, который паук получает в ответ на запросы URL из start_urls.

Запуск паука:

scrapy crawl myspider

Для работы с пагинацией можно использовать несколько подходов. Один из них — это прямое следование по ссылкам на следующие страницы. Можно реализовать, извлекая URL след. страницы и передавая его в метод follow Scrapy. Например:

def parse(self, response):     # обработка данных страницы     ...     next_page = response.css("li.next a::attr(href)").get()     if next_page is not None:         yield response.follow(next_page, self.parse)

Для автоматизации входа на сайты можно использовать формы входа, автоматом заполняя и отправляя данные форм. Scrapy поддерживает отправку форм с помощью класса FormRequest:

from scrapy.http import FormRequest  def start_requests(self):     return [FormRequest("http://example.com/login", formdata={'user': 'john', 'pass': 'secret'}, callback=self.logged_in)]  def logged_in(self, response):     # продолжение скрапинга после входа     pass

Для страниц, где контент загружается с помощью JS, можно использовать Splash или Selenium, которые позволяют исполнять JS перед извлечением данных. Например, тот же Splash интегрируется с Scrapy через scrapy-splash, позволяя выполнять JavaScript и получать актуальный HTML. Пример:

from scrapy_splash import SplashRequest  def start_requests(self):     yield SplashRequest(url='http://example.com', callback=self.parse, endpoint='render.html', args={'wait': 0.5})  def parse(self, response):     # извлечение данных     pass

Scrapy поддерживает экспорт данных в форматы JSON, CSV и XML с помощью встроенных экспортеров. Экспорт данных может быть настроен через settings.py проекта с помощью параметра FEEDS, который позволяет указать различные настройки экспорта для каждого типа файла:

# настройка экспорта данных в JSON и CSV FEEDS = {     'items.json': {         'format': 'json',         'encoding': 'utf8',         'store_empty': False,         'fields': None,         'indent': 4,     },     'items.csv': {         'format': 'csv',         'fields': ['field1', 'field2', 'field3'],  # спецификация полей для экспорта     } }

Item Pipelines в Scrapy позволяют проводить послепроцессинг собранных данных, включая их валидацию, очистку и сохранение в БД. Например, для сохранения данных в БД MySQL или PostgreSQL, можно настроить pipeline, которая будет обрабатывать каждый собранный элемент:

import psycopg2  class PostgresPipeline(object):     def open_spider(self, spider):         hostname = 'localhost'         username = 'postgres'         password = 'yourpassword'         database = 'scrapy_db'         self.connection = psycopg2.connect(host=hostname, user=username, password=password, dbname=database)         self.cur = self.connection.cursor()      def process_item(self, item, spider):         self.cur.execute("INSERT INTO table_name (field1, field2) VALUES (%s, %s)",                           (item['field1'], item['field2']))         self.connection.commit()         return item      def close_spider(self, spider):         self.cur.close()         self.connection.close()

Этот pipeline устанавливает соединение с БД при открытии паука, обрабатывает каждый элемент при его сборе, вставляя данные в таблицу, и закрывает соединение после завершения работы паука.

Пример использования

Создадим парсер, который будет будет собирать данные с вымышленного сайта книжного магазина, который имеет пагинацию, требует авторизации для доступа к некоторым страницам и использует JS для отображения некоторых элементов.

Создадим новый проект Scrapy:

scrapy startproject books_scraper

Переходим в директорию проекта:

cd books_scraper

В директории spiders создадим нового паука book_spider.py:

import scrapy from scrapy_splash import SplashRequest from scrapy.http import FormRequest from books_scraper.items import BookItem  # нужен класс Item в items.py  class BookSpider(scrapy.Spider):     name = 'book_spider'     login_url = 'http://books.example.com/login'     start_urls = ['http://books.example.com']      def start_requests(self):         #переход на страницу входа         yield scrapy.Request(self.login_url, self.parse_login)      def parse_login(self, response):         # отправка данных для входа         yield FormRequest.from_response(response,                                         formdata={'username': 'user', 'password': 'pass'},                                         callback=self.after_login)      def after_login(self, response):         # проверка успешного входа и продолжение скрапинга         if "authentication failed" in response.body:             self.logger.error("Login failed")             return         else:             # запуск скрапинга с использованием Splash для обработки JavaScript             for url in self.start_urls:                 yield SplashRequest(url, self.parse, endpoint='execute', args={'lua_source': self.lua_script()})      def parse(self, response):         # обработка данных страницы         books = response.css('div.book')         for book in books:             item = BookItem()             item['title'] = book.css('h3.title::text').get()             item['price'] = book.css('p.price::text').get()             yield item          # пагинация         next_page = response.css('li.next a::attr(href)').get()         if next_page:             yield SplashRequest(next_page, self.parse, endpoint='execute', args={'lua_source': self.lua_script()})      def lua_script(self):         # Lua скрипт для Splash, скроллинг страницы или другие действия         return """         function main(splash)             splash:set_user_agent(splash.args.ua)             assert(splash:go(splash.args.url))             assert(splash:wait(1))             return {html = splash:html()}         end         """

В файле items.py создадим структуру данных для книг:

import scrapy  class BookItem(scrapy.Item):     title = scrapy.Field()     price = scrapy.Field()

В pipelines.py добавим код для сохранения данных в PostgreSQL:

import psycopg2  class PostgresPipeline(object):     def open_spider(self, spider):         self.connection = psycopg2.connect(host='localhost', dbname='scrapy_books', user='postgres', password='password')         self.cur = self.connection.cursor()      def close_spider(self, spider):         self.connection.close()      def process_item(self, item, spider):         self.cur.execute("INSERT INTO books (title, price) VALUES (%s, %s)", (item['title'], item['price']))         self.connection.commit()         return item

Активируем PostgresPipeline в settings.py и настраивем Splash в случае работы с JS:

ITEM_PIPELINES = {    'books_scraper.pipelines.PostgresPipeline': 300, }  SPLASH_URL = 'http://localhost:8050'

Запускаем:

scrapy crawl book_spider

Подробней с Scrapy можно ознакомиться в документации.

В заключение расскажу об открытых уроках:

  • 13 мая: Генераторы и очереди в Python: рассмотрим концепции генераторных функций и очередей, поговорим о сходствах, различиях, и применимости в решении реальных задач. Записаться

  • 28 мая: Самые эффективные однострочники в Python: поделимся секретами и мастерством создания мощных однострочников в Python. Записаться


ссылка на оригинал статьи https://habr.com/ru/articles/812035/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *