└── README.md /README.md: -------------------------------------------------------------------------------- 1 | [![made-with-markdown](https://img.shields.io/badge/Made%20with-Markdown-1f425f.svg)](https://daringfireball.net/projects/markdown/) 2 | [![Chat](https://img.shields.io/badge/chat-t.me%2Fscrapy__python-blue.svg)](https://t.me/scrapy_python) 3 | [![GitHub stars](https://img.shields.io/github/stars/bulatbulat48/ru-scrapy-python.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/bulatbulat48/ru-scrapy-python/stargazers/) 4 | [![GitHub contributors](https://img.shields.io/github/contributors/bulatbulat48/ru-scrapy-python.svg)](https://GitHub.com/bulatbulat48/ru-scrapy-python/graphs/contributors/) 5 | 6 | # [@scrapy_python](https://t.me/scrapy_python) FAQ # 7 | 8 | В этом репозитории находится полезная информация, собранная участниками чата. 9 | 10 | ### С чего начать? ### 11 | 12 | * [Прочитать документацию](https://docs.scrapy.org/en/latest/) 13 | * [Очень рекомендуется пройти туториал](https://docs.scrapy.org/en/latest/intro/tutorial.html) 14 | * Базовые вопросы по питону [@ru_python_beginners](https://t.me/ru_python_beginners) 15 | * Основной чат по scrapy в ТГ: [@scrapy_python](https://t.me/scrapy_python) 16 | 17 | ### Как ограничить количество реквестов? ### 18 | 19 | * [CLOSESPIDER_PAGECOUNT = 10](https://docs.scrapy.org/en/latest/topics/extensions.html#closespider-pagecount) 20 | * И там же рядом полезные настройки вида: CLOSESPIDER_ITEMCOUNT, CLOSESPIDER_ERRORCOUNT, CLOSESPIDER_TIMEOUT, CLOSESPIDER_TIMEOUT_NO_ITEM 21 | 22 | ### Как спарсить данные из JS? ### 23 | 24 | * смотреть откуда идут данные в Chrome -> devtools -> network -> XHR 25 | * [JS to Python](http://piter.io/projects/js2py) 26 | * Официальная документация [рекомендует](https://docs.scrapy.org/en/latest/topics/dynamic-content.html) 27 | * ставится Splash(удобно в Docker) и плагин [scrapy_splash](https://github.com/scrapy-plugins/scrapy-splash) (устарело) 28 | * Сейчас широко поддерживается и используется [scrapy-playwright](https://github.com/scrapy-plugins/scrapy-playwright), под windows работает только из-под WSL2. 29 | 30 | ### Лучшие практики ### 31 | 32 | * Использовать css селекторы чтобы избежать пробелов в названии при использовании @class в xpath, альтернатива "contains(@class, 'someclass')" выглядит сложнее. 33 | * Использовать xpath для поиска сложных значений, например в таблицах 34 | * Использовать [корутины](https://docs.scrapy.org/en/latest/topics/coroutines.html) или [asyncio](https://docs.scrapy.org/en/latest/topics/asyncio.html) для синхронных запросов в функции. Добавлен пример с [asyncio](https://github.com/bulatbulat48/ru-scrapy-python/blob/master/README.md#%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%BD%D1%8B%D1%85-%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%BE%D0%B2-%D1%81-asyncio-%D0%B4%D0%BB%D1%8F-%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD%D1%8B-inline_requests). 35 | * Посмотреть мобильную версию 36 | 37 | ### Популярные css селекторы ### 38 | * Достать href тега a: "a::attr(href)" 39 | * Достать текст ноды: "title::text" 40 | * Аналог contains у xpath, "a[href*=image] img::attr(src)" 41 | * Можно добавить xpath ".css('img').xpath('@src')" 42 | * [Проверка css-селекторов](https://www.w3schools.com/cssref/trysel.asp) 43 | * [Список css-селекторов](https://www.w3schools.com/cssref/css_selectors.asp) 44 | 45 | ### Пример xpath селекторов, которые обычно не вытащишь через css-селекторы ### 46 | * Получить предка от текущего элемента, например: `response.xpath('./ancestor::a[contains(@class, "class_name")]/@href` 47 | * Комментарии html, например: `response.xpath('.//ul[@class="class_name"]/comment()[contains(.,"Артикул")]').get()` 48 | 49 | ### Полезные библиотеки ### 50 | * [html_text](https://github.com/TeamHG-Memex/html-text) - извлечь текст из сложного селектора, аналог .get_text(' ', strip=True) из BeautifulSoup, но быстрее и точнее. 51 | 52 | ### Полезные браузерные расширения ### 53 | * [Selector Gadget](https://selectorgadget.com/) получить короткий css или xpath элемента(ов), см. видео на их сайте. Получается намного лучше встроенного в браузер copy as css/xpath. 54 | 55 | ### Нельзя мешать yield и return? ### 56 | После return жизни нет. Нужно возвращать список или что-то итерируемое. 57 | 58 | ### Как вытащить узел по тексту внутри него используя css-селектор ### 59 | Через CSS - никак. Использовать xpath contains. [Документация по xpath](http://www.zvon.org/comp/r/tut-XPath_1.html). 60 | 61 | ### Как поставить на windows ### 62 | Простой способ - поставить в anaconda 63 | 64 | ### Как достать items из последнего job-а в scrapinghub? ### 65 | https://app.scrapinghub.com/api/items.json?project=PROJECT&spider=SPIDERNAME&apikey=KEY 66 | там где SPIDERNAME нужно вставить именно название, а не номер паука. 67 | дополнительно можно почитать [тут](https://support.scrapinghub.com/support/solutions/articles/22000200409-fetching-latest-spider-data) 68 | 69 | ### Как спарсить данные из нескольких форм с POST-запросами ### 70 | Использовать цикл по форме c FormRequest.from_response, дополнительное поле со счетчиком формы formnumber=counter и с фильтром dont_filter=True. 71 | 72 | ### Как обойти Cloudflare? ### 73 | Страница отдает 503 ошибку. На этой странице javascript собирает код в форму с рандомным урлом и тремя hidden полями. После отправки этой формы отдается 302 редирект на нужную страницу. 74 | 75 | ### Как передавать cookies ### 76 | При надобности в передаче заранее подготовленных (например после авторизации на сайте) cookies, осуществить это можно через свой DownloaderMiddleware так: 77 | * В settings.py активируйте ваши DOWNLOADER_MIDDLEWARES 78 | * В settings.py убедиться, что значение по умолчанию `COOKIES_ENABLED = True` не переопределено на False, иначе scrapy не будет сохранять передаваемые ему страницой cookies. 79 | * В middlewares.py в методе обработки запросов process_request вашего DownloaderMiddleware прописать что-то такое: 80 | ```python 81 | def process_request(self, request, spider): 82 | request.cookies[cookiename] = value # вставьте ваши значения 83 | return None 84 | ``` 85 | * `COOKIES_DEBUG = True` в settings.py может помочь увидеть, что же происходит. 86 | 87 | ### Где найти дефолтные настройки Scrapy? ### 88 | [default_settings.py в офф.репо](https://github.com/scrapy/scrapy/blob/master/scrapy/settings/default_settings.py) 89 | 90 | ### Как проанализировать запрос/форму? ### 91 | Chrome -> devtools -> network -> клик на страницу -> copy as curl. Далее гуглим ["curl to python"](https://curlconverter.com/python/), вставляем код и получаем распаршенный код в библиотеке requests 92 | Если в `Network` в браузере поставить галочку напротив `preserve log`, то история запросов перестает очищаться при переходах между страницами. 93 | 94 | ### Чем проанализировать пакеты сети или воспроизвести запрос/форму? ### 95 | Fiddler или postman(он умеет сразу в питонкод конвертить). Мощнее и сложнее wireshark. 96 | 97 | ### Обработка [кодов состояния HTTP](https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D0%BA%D0%BE%D0%B4%D0%BE%D0%B2_%D1%81%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D1%8F_HTTP) ### 98 | [По умолчанию скрапи обрабатывает успешные ответы](https://docs.scrapy.org/en/latest/topics/spider-middleware.html#module-scrapy.spidermiddlewares.httperror), для обработки остальных ответов используйте `handle_httpstatus_list`, например: 99 | 100 | ```python 101 | class MySpider(CrawlSpider): 102 | handle_httpstatus_list = [404] 103 | ``` 104 | - также пригождается в редких случаях, если сайт отдает ошибку, но сам при этом показывает валидные данные, а scrapy ему "верит" - ответ не 200, и не парсит. 105 | ### params в scrapy 106 | В [requests](https://requests.readthedocs.io/en/master/user/quickstart/#passing-parameters-in-urls) можно передать дополнительные параметры в GET методе: 107 | 108 | ```python 109 | import requests 110 | params = [('q', 'scrapy')] 111 | response = requests.get('https://github.com/search', params=params) 112 | ``` 113 | В [scrapy](https://docs.scrapy.org/en/latest/topics/request-response.html?highlight=FormRequest#formrequest-objects) можно сделать аналогично, через FormRequest: 114 | ```python 115 | FormRequest( 116 | url='https://github.com/search', 117 | method='GET', 118 | formdata=params, 119 | callback=self.parse_data, 120 | ) 121 | ``` 122 | 123 | ### Пример синхронного запроса с использованием async/await синтаксиса: 124 | Для одного запроса: 125 | ```Python 126 | from scrapy.utils.defer import maybe_deferred_to_future 127 | class SingleRequestSpider(scrapy.Spider): 128 | name = "example" 129 | allowed_domains = ["https://books.toscrape.com"] 130 | start_urls = ["https://books.toscrape.com/catalogue/page-1.html"] 131 | 132 | async def parse(self, response, **kwargs): 133 | additional_request = scrapy.Request("https://books.toscrape.com/catalogue/the-black-maria_991/index.html") 134 | deferred = self.crawler.engine.download(additional_request) 135 | additional_response = await maybe_deferred_to_future(deferred) 136 | yield { 137 | "h1": response.css("h1::text").get(), 138 | "price": additional_response.css(".price_color::text").get(), 139 | } 140 | ``` 141 | 142 | ### Пример параллельных запросов с использованием asyncio: 143 | ```Python 144 | import scrapy 145 | from scrapy.utils.defer import maybe_deferred_to_future 146 | import asyncio 147 | 148 | class MultipleRequestsSpider(scrapy.Spider): 149 | name = "multiple" 150 | start_urls = ["https://books.toscrape.com/catalogue/page-1.html"] 151 | 152 | @classmethod 153 | def update_settings(cls, settings): 154 | settings["TWISTED_REACTOR"] = "twisted.internet.asyncioreactor.AsyncioSelectorReactor" 155 | 156 | async def parse(self, response, **kwargs): 157 | additional_requests = [ 158 | scrapy.Request("https://books.toscrape.com/catalogue/the-black-maria_991/index.html"), 159 | scrapy.Request("https://books.toscrape.com/catalogue/the-requiem-red_995/index.html"), 160 | ] 161 | coroutines = [] 162 | for r in additional_requests: 163 | deferred = self.crawler.engine.download(r) 164 | coroutines.append(maybe_deferred_to_future(deferred)) 165 | responses = await asyncio.gather( 166 | *coroutines, return_exceptions=True 167 | ) 168 | yield { 169 | 'h1': response.css('h1::text').get(), 170 | 'price': responses[0].css('.price_color::text').get(), 171 | 'price2': responses[1].css('.price_color::text').get(), 172 | } 173 | ``` 174 | 175 | ### Деплой Scrapy ### 176 | * Хостинг [Scrapinghub](https://scrapinghub.com/) по дефолту стоит задержка, нужно отключать в настройках AUTOTHROTTLE_ENABLED чекбокс False 177 | * UI для Scrapy [ScrapydWeb](https://github.com/my8100/scrapydweb) 178 | * Управление [Scrapyd](https://github.com/scrapy/scrapyd) 179 | 180 | ## Тесты ### 181 | * [Spidermon](https://github.com/scrapinghub/spidermon) 182 | 183 | ### На сколько Scrapy быстрый? ### 184 | Проверка N страниц. 185 | * requests в один поток - бесконечное время 186 | * scrapy из локальной машины - 30 минут 187 | * scrapinghub с включенным по дефолту тротлингом - больше 1 часа 188 | * scrapinghub без троттлинга 1 юнит - 23 минуты 189 | * scrapinghub без троттлинга 3 юнита - 15 минут 190 | 191 | ### Можно ли использовать регулярные выражения в xpath? ### 192 | Да, [можно](https://docs.scrapy.org/en/latest/topics/selectors.html#using-exslt-extensions) 193 | 194 | ### Практика по регулярным выражениям. С чего начать? ### 195 | * Два туториала от Corey Shaffer: [How to Match Any Pattern of Text](https://www.youtube.com/watch?v=sa-TUpSx1JA) и [How to Write and Match Regular Expressions](https://www.youtube.com/watch?v=K8L6KVGG-7o) 196 | * [Mastering Python Regular Expressions](https://www.amazon.com/Mastering-Python-Regular-Expressions-Felix/dp/1783283157/) 197 | * [Тираногайд](https://www.rexegg.com/) по регуляркам 198 | * ChatGPT и его аналоги - описываете, что вам надо, заодно пихаете им примеры, что у вас на входе, и что вам надо на выходе, и обычно оно справляется. 199 | 200 | ### Очистка текста от HTML тегов ### 201 | Исходный текст 202 | ```html 203 |

Включает:

Клапан впускной / VALVE INLET АРТ: 3142H111 3 шт

204 | ``` 205 | 206 | Удаление HTML тегов из текста без сохранения визуального переноса строк: 207 | ```python 208 | from w3lib.html import remove_tags 209 | remove_tags(text) 210 | Включает:Клапан впускной / VALVE INLET АРТ: 3142H111 3 шт 211 | ``` 212 | 213 | Удаление тегов из текста с сохранением визуального переноса строк с помощью библиотеки html2text 214 | ```python 215 | import html2text 216 | html2text.html2text(text) 217 | Клапан впускной / VALVE INLET АРТ: 3142H111 3 шт 218 | ``` 219 | 220 | ### Полезные ресурсы по Xpath ### 221 | Справочники и туториалы с примерами: 222 | * [Отличный гайд для начинающих от Guru99](https://www.guru99.com/xpath-selenium.html) 223 | * [Интро от W3Schools](https://www.w3schools.com/xml/xpath_intro.asp) 224 | * [Справочник-гайд от Javatpoint](https://www.javatpoint.com/xpath-tutorial) 225 | * [Туториал по работе с xpath и xslt в библитеке lxml](https://lxml.de/xpathxslt.html) 226 | * [от Zvon](http://zvon.org/comp/r/tut-XPath_1.html) 227 | * [от TutorialsPoint](https://www.tutorialspoint.com/xpath/) 228 | 229 | Подборка cheatsheets и bestpractices по xpath 230 | * [Подборка cheatsheets](http://scraping.pro/5-best-xpath-cheat-sheets-and-quick-references/) 231 | * [Cheatsheet с примерами](https://devhints.io/xpath) 232 | * [Лучшие практики](https://hackernoon.com/xpath-tips-from-the-web-scraping-trenches-fda06b0bf0a8) 233 | * [Тренажер XPATH](https://www.freeformatter.com/xpath-tester.html) 234 | --------------------------------------------------------------------------------