Привет, Хабр! Сегодня мы поговорим о хорошем инструменте для веб-скрапинга, который зарекомендовал себя, библиотеке 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/
Добавить комментарий