@scrapy_python FAQ
В этом репозитории находится полезная информация, собранная участниками чата.
- Прочитать документацию
- Очень рекомендуется пройти туториал
- Базовые вопросы по питону @ru_python_beginners
- Основной чат по scrapy в ТГ: @scrapy_python
- CLOSESPIDER_PAGECOUNT = 10
- И там же рядом полезные настройки вида: CLOSESPIDER_ITEMCOUNT, CLOSESPIDER_ERRORCOUNT, CLOSESPIDER_TIMEOUT, CLOSESPIDER_TIMEOUT_NO_ITEM
- смотреть откуда идут данные в Chrome -> devtools -> network -> XHR
- JS to Python
- Официальная документация рекомендует
- ставится Splash(удобно в Docker) и плагин scrapy_splash (устарело)
- Сейчас широко поддерживается и используется scrapy-playwright, под windows работает только из-под WSL2.
- Использовать css селекторы чтобы избежать пробелов в названии при использовании @class в xpath, альтернатива "contains(@class, 'someclass')" выглядит сложнее.
- Использовать xpath для поиска сложных значений, например в таблицах
- Использовать корутины или asyncio для синхронных запросов в функции. Добавлен пример с asyncio.
- Посмотреть мобильную версию
- Достать href тега a: "a::attr(href)"
- Достать текст ноды: "title::text"
- Аналог contains у xpath, "a[href*=image] img::attr(src)"
- Можно добавить xpath ".css('img').xpath('@src')"
- Проверка css-селекторов
- Список css-селекторов
- Получить предка от текущего элемента, например:
response.xpath('./ancestor::a[contains(@class, "class_name")]/@href
- Комментарии html, например:
response.xpath('.//ul[@class="class_name"]/comment()[contains(.,"Артикул")]').get()
- html_text - извлечь текст из сложного селектора, аналог .get_text(' ', strip=True) из BeautifulSoup, но быстрее и точнее.
- Selector Gadget получить короткий css или xpath элемента(ов), см. видео на их сайте. Получается намного лучше встроенного в браузер copy as css/xpath.
После return жизни нет. Нужно возвращать список или что-то итерируемое.
Через CSS - никак. Использовать xpath contains. Документация по xpath.
Простой способ - поставить в anaconda
https://app.scrapinghub.com/api/items.json?project=PROJECT&spider=SPIDERNAME&apikey=KEY там где SPIDERNAME нужно вставить именно название, а не номер паука. дополнительно можно почитать тут
Использовать цикл по форме c FormRequest.from_response, дополнительное поле со счетчиком формы formnumber=counter и с фильтром dont_filter=True.
Страница отдает 503 ошибку. На этой странице javascript собирает код в форму с рандомным урлом и тремя hidden полями. После отправки этой формы отдается 302 редирект на нужную страницу.
При надобности в передаче заранее подготовленных (например после авторизации на сайте) cookies, осуществить это можно через свой DownloaderMiddleware так:
- В settings.py активируйте ваши DOWNLOADER_MIDDLEWARES
- В settings.py убедиться, что значение по умолчанию
COOKIES_ENABLED = True
не переопределено на False, иначе scrapy не будет сохранять передаваемые ему страницой cookies. - В middlewares.py в методе обработки запросов process_request вашего DownloaderMiddleware прописать что-то такое:
def process_request(self, request, spider):
request.cookies[cookiename] = value # вставьте ваши значения
return None
COOKIES_DEBUG = True
в settings.py может помочь увидеть, что же происходит.
default_settings.py в офф.репо
Chrome -> devtools -> network -> клик на страницу -> copy as curl. Далее гуглим "curl to python", вставляем код и получаем распаршенный код в библиотеке requests
Если в Network
в браузере поставить галочку напротив preserve log
, то история запросов перестает очищаться при переходах между страницами.
Fiddler или postman(он умеет сразу в питонкод конвертить). Мощнее и сложнее wireshark.
Обработка кодов состояния HTTP
По умолчанию скрапи обрабатывает успешные ответы, для обработки остальных ответов используйте handle_httpstatus_list
, например:
class MySpider(CrawlSpider):
handle_httpstatus_list = [404]
- также пригождается в редких случаях, если сайт отдает ошибку, но сам при этом показывает валидные данные, а scrapy ему "верит" - ответ не 200, и не парсит.
В requests можно передать дополнительные параметры в GET методе:
import requests
params = [('q', 'scrapy')]
response = requests.get('https://github.com/search', params=params)
В scrapy можно сделать аналогично, через FormRequest:
FormRequest(
url='https://github.com/search',
method='GET',
formdata=params,
callback=self.parse_data,
)
Для одного запроса:
from scrapy.utils.defer import maybe_deferred_to_future
class SingleRequestSpider(scrapy.Spider):
name = "example"
allowed_domains = ["https://books.toscrape.com"]
start_urls = ["https://books.toscrape.com/catalogue/page-1.html"]
async def parse(self, response, **kwargs):
additional_request = scrapy.Request("https://books.toscrape.com/catalogue/the-black-maria_991/index.html")
deferred = self.crawler.engine.download(additional_request)
additional_response = await maybe_deferred_to_future(deferred)
yield {
"h1": response.css("h1::text").get(),
"price": additional_response.css(".price_color::text").get(),
}
import scrapy
from scrapy.utils.defer import maybe_deferred_to_future
import asyncio
class MultipleRequestsSpider(scrapy.Spider):
name = "multiple"
start_urls = ["https://books.toscrape.com/catalogue/page-1.html"]
@classmethod
def update_settings(cls, settings):
settings["TWISTED_REACTOR"] = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"
async def parse(self, response, **kwargs):
additional_requests = [
scrapy.Request("https://books.toscrape.com/catalogue/the-black-maria_991/index.html"),
scrapy.Request("https://books.toscrape.com/catalogue/the-requiem-red_995/index.html"),
]
coroutines = []
for r in additional_requests:
deferred = self.crawler.engine.download(r)
coroutines.append(maybe_deferred_to_future(deferred))
responses = await asyncio.gather(
*coroutines, return_exceptions=True
)
yield {
'h1': response.css('h1::text').get(),
'price': responses[0].css('.price_color::text').get(),
'price2': responses[1].css('.price_color::text').get(),
}
- Хостинг Scrapinghub по дефолту стоит задержка, нужно отключать в настройках AUTOTHROTTLE_ENABLED чекбокс False
- UI для Scrapy ScrapydWeb
- Управление Scrapyd
Проверка N страниц.
- requests в один поток - бесконечное время
- scrapy из локальной машины - 30 минут
- scrapinghub с включенным по дефолту тротлингом - больше 1 часа
- scrapinghub без троттлинга 1 юнит - 23 минуты
- scrapinghub без троттлинга 3 юнита - 15 минут
Да, можно
- Два туториала от Corey Shaffer: How to Match Any Pattern of Text и How to Write and Match Regular Expressions
- Mastering Python Regular Expressions
- Тираногайд по регуляркам
- ChatGPT и его аналоги - описываете, что вам надо, заодно пихаете им примеры, что у вас на входе, и что вам надо на выходе, и обычно оно справляется.
Исходный текст
<p>Включает:</p><p>Клапан впускной / VALVE INLET АРТ: 3142H111 3 шт</p>
Удаление HTML тегов из текста без сохранения визуального переноса строк:
from w3lib.html import remove_tags
remove_tags(text)
Включает:Клапан впускной / VALVE INLET АРТ: 3142H111 3 шт
Удаление тегов из текста с сохранением визуального переноса строк с помощью библиотеки html2text
import html2text
html2text.html2text(text)
Клапан впускной / VALVE INLET АРТ: 3142H111 3 шт
Справочники и туториалы с примерами:
- Отличный гайд для начинающих от Guru99
- Интро от W3Schools
- Справочник-гайд от Javatpoint
- Туториал по работе с xpath и xslt в библитеке lxml
- от Zvon
- от TutorialsPoint
Подборка cheatsheets и bestpractices по xpath