Запросы и ответы¶
Scrapy использует объекты Request
и Response
для сканирования веб-сайтов.
Обычно объекты Request
генерируются в пауках и проходят через систему, пока не достигнут загрузчика, который выполняет запрос и возвращает объект Response
, который возвращается к пауку, отправившему запрос.
Оба класса Request
и Response
имеют подклассы, которые добавляют функциональность, не требуемую в базовых классах. Они описаны ниже в Запросить подклассы и Подклассы Response.
Объекты запроса¶
Передача дополнительных данных в функции обратного вызова¶
Обратный вызов запроса — это функция, которая будет вызываться при загрузке ответа на данный запрос. Функция обратного вызова будет вызываться с загруженным объектом Response
в качестве первого аргумента.
Пример:
def parse_page1(self, response):
return scrapy.Request("http://www.example.com/some_page.html",
callback=self.parse_page2)
def parse_page2(self, response):
# this would log http://www.example.com/some_page.html
self.logger.info("Visited %s", response.url)
В некоторых случаях вам может быть интересно передать аргументы этим функциям обратного вызова, чтобы вы могли получить аргументы позже, во втором обратном вызове. В следующем примере показано, как этого добиться с помощью атрибута Request.cb_kwargs
:
def parse(self, response):
request = scrapy.Request('http://www.example.com/index.html',
callback=self.parse_page2,
cb_kwargs=dict(main_url=response.url))
request.cb_kwargs['foo'] = 'bar' # add more arguments for the callback
yield request
def parse_page2(self, response, main_url, foo):
yield dict(
main_url=main_url,
other_url=response.url,
foo=foo,
)
Осторожно
Request.cb_kwargs
был представлен в версии 1.7
. До этого для передачи информации по обратным вызовам рекомендовалось использовать Request.meta
. После 1.7
предпочтительным способом обработки пользовательской информации стал Request.cb_kwargs
, оставив Request.meta
для связи с такими компонентами, как промежуточное ПО и расширения.
Использование errbacks для перехвата исключений при обработке запроса¶
Ошибка запроса — это функция, которая будет вызываться при возникновении исключения при его обработке.
Он получает Failure
в качестве первого параметра и может использоваться для отслеживания тайм-аутов установления соединения, ошибок DNS и т. д.
Вот пример паука, который регистрирует все ошибки и при необходимости улавливает некоторые конкретные ошибки:
import scrapy
from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError, TCPTimedOutError
class ErrbackSpider(scrapy.Spider):
name = "errback_example"
start_urls = [
"http://www.httpbin.org/", # HTTP 200 expected
"http://www.httpbin.org/status/404", # Not found error
"http://www.httpbin.org/status/500", # server issue
"http://www.httpbin.org:12345/", # non-responding host, timeout expected
"https://example.invalid/", # DNS error expected
]
def start_requests(self):
for u in self.start_urls:
yield scrapy.Request(u, callback=self.parse_httpbin,
errback=self.errback_httpbin,
dont_filter=True)
def parse_httpbin(self, response):
self.logger.info('Got successful response from {}'.format(response.url))
# do something useful here...
def errback_httpbin(self, failure):
# log all failures
self.logger.error(repr(failure))
# in case you want to do something special for some errors,
# you may need the failure's type:
if failure.check(HttpError):
# these exceptions come from HttpError spider middleware
# you can get the non-200 response
response = failure.value.response
self.logger.error('HttpError on %s', response.url)
elif failure.check(DNSLookupError):
# this is the original request
request = failure.request
self.logger.error('DNSLookupError on %s', request.url)
elif failure.check(TimeoutError, TCPTimedOutError):
request = failure.request
self.logger.error('TimeoutError on %s', request.url)
Доступ к дополнительным данным в функциях errback¶
В случае сбоя обработки запроса вас может заинтересовать доступ к аргументам функций обратного вызова, чтобы вы могли продолжить обработку на основе аргументов в errback. В следующем примере показано, как этого добиться с помощью Failure.request.cb_kwargs
:
def parse(self, response):
request = scrapy.Request('http://www.example.com/index.html',
callback=self.parse_page2,
errback=self.errback_page2,
cb_kwargs=dict(main_url=response.url))
yield request
def parse_page2(self, response, main_url):
pass
def errback_page2(self, failure):
yield dict(
main_url=failure.request.cb_kwargs['main_url'],
)
Специальные ключи Request.meta¶
Атрибут Request.meta
может содержать любые произвольные данные, но есть некоторые специальные ключи, распознаваемые Scrapy и его встроенными расширениями.
Далее их перечисление:
dont_merge_cookies
ftp_password
(для получения дополнительной информации см.FTP_PASSWORD
)ftp_user
(Подробнее см.FTP_USER
)
bindaddress¶
IP-адрес исходящего IP-адреса, который будет использоваться для выполнения запроса.
download_timeout¶
Время (в секундах), в течение которого загрузчик будет ждать до истечения времени ожидания. См. также: DOWNLOAD_TIMEOUT
.
download_latency¶
Время, затраченное на получение ответа с момента запуска запроса, т. е. HTTP-сообщения, отправленного по сети. Данный мета-ключ становится доступным только после загрузки ответа. Хотя большинство других мета-ключей используются для управления поведением Scrapy, предполагается, что данный ключ предназначен только для чтения.
download_fail_on_dataloss¶
Независимо от того, терпеть ли неудачу из-за неработающих ответов. Смотрите: DOWNLOAD_FAIL_ON_DATALOSS
.
max_retry_times¶
Мета-ключ используется для установки времени повтора для каждого запроса. При инициализации мета-ключ max_retry_times
имеет более высокий приоритет над параметром RETRY_TIMES
.
Остановка загрузки ответа¶
Вызов исключения StopDownload
из обработчика сигналов bytes_received
или headers_received
остановит загрузку данного ответа. См. следующий пример:
import scrapy
class StopSpider(scrapy.Spider):
name = "stop"
start_urls = ["https://docs.scrapy.org/en/latest/"]
@classmethod
def from_crawler(cls, crawler):
spider = super().from_crawler(crawler)
crawler.signals.connect(spider.on_bytes_received, signal=scrapy.signals.bytes_received)
return spider
def parse(self, response):
# 'last_chars' show that the full response was not downloaded
yield {"len": len(response.text), "last_chars": response.text[-40:]}
def on_bytes_received(self, data, request, spider):
raise scrapy.exceptions.StopDownload(fail=False)
что даёт следующий результат:
2020-05-19 17:26:12 [scrapy.core.engine] INFO: Spider opened
2020-05-19 17:26:12 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2020-05-19 17:26:13 [scrapy.core.downloader.handlers.http11] DEBUG: Download stopped for <GET https://docs.scrapy.org/en/latest/> from signal handler StopSpider.on_bytes_received
2020-05-19 17:26:13 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://docs.scrapy.org/en/latest/> (referer: None) ['download_stopped']
2020-05-19 17:26:13 [scrapy.core.scraper] DEBUG: Scraped from <200 https://docs.scrapy.org/en/latest/>
{'len': 279, 'last_chars': 'dth, initial-scale=1.0">\n \n <title>Scr'}
2020-05-19 17:26:13 [scrapy.core.engine] INFO: Closing spider (finished)
По умолчанию результирующие ответы обрабатываются соответствующими ошибками. Чтобы вместо этого вызвать их обратный вызов, как в этом примере, передать fail=False
в исключение StopDownload
.
Запросить подклассы¶
Вот список встроенных подклассов Request
. Вы также можете создать подкласс для реализации своих собственных функций.
Объекты FormRequest¶
Класс FormRequest расширяет базовый Request
функциями для работы с формами HTML. Он использует формы lxml.html для предварительного заполнения полей формы данными из объектов Response
.
- class scrapy.http.request.form.FormRequest¶
- class scrapy.http.FormRequest¶
- class scrapy.FormRequest(url[, formdata, ...])¶
Класс
FormRequest
добавляет новый ключевой параметр к методу__init__
. Остальные аргументы такие же, как для классаRequest
, и здесь не описаны.- Параметры
formdata (dict or collections.abc.Iterable) – is a dictionary (or iterable of (key, value) tuples) containing HTML Form data which will be url-encoded and assigned to the body of the request.
Объекты
FormRequest
поддерживают следующий метод класса в дополнение к стандартным методамRequest
:- classmethod FormRequest.from_response(response[, formname=None, formid=None, formnumber=0, formdata=None, formxpath=None, formcss=None, clickdata=None, dont_click=False, ...])¶
Возвращает новый объект
FormRequest
со значениями поля формы, предварительно заполненными значениями, найденными в элементе HTML<form>
, содержащемся в данном ответе. Для примера см. Использование FormRequest.from_response() для имитации входа пользователя в систему.Политика заключается в том, чтобы по умолчанию автоматически имитировать щелчок на любом элементе управления формы, который выглядит интерактивным, например
<input type="submit">
. Несмотря на то, что это довольно удобно и часто является желаемым поведением, иногда это может вызвать проблемы, которые может быть трудно отладить. Например, при работе с формами, которые заполняются и/или отправляются с использованием javascript, поведениеfrom_response()
по умолчанию может быть не самым подходящим. Чтобы отключить это поведение, вы можете установить для аргументаdont_click
значениеTrue
. Кроме того, если вы хотите изменить выбранный элемент управления (вместо его отключения), вы также можете использовать аргументclickdata
.Осторожно
Использование этого метода с элементами select, у которых есть начальные или конечные пробелы в значениях параметров, не будет работать из-за ошибки в lxml, который должен быть исправлен в lxml 3.8 и выше.
- Параметры
response (
Response
object) – ответ, содержащий форму HTML, которая будет использоваться для предварительного заполнения полей формыformname (str) – если задано, будет использоваться форма с атрибутом name, установленным на это значение.
formid (str) – если задано, будет использоваться форма с атрибутом id, установленным на это значение.
formxpath (str) – если задано, будет использоваться первая форма, соответствующая xpath.
formcss (str) – если задано, будет использоваться первая форма, соответствующая селектору css.
formnumber (int) – номер формы для использования, если ответ содержит несколько форм. Первый (также по умолчанию) —
0
.formdata (dict) – поля для переопределения в данных формы. Если поле уже присутствовало в элементе ответа
<form>
, его значение заменяется значением, переданным в этом параметре. Если в данный параметр передано значениеNone
, поле не будет включено в запрос, даже если оно присутствовало в элементе ответа<form>
.clickdata (dict) – атрибуты для поиска нажатого элемента управления. Если он не указан, данные формы будут отправлены, имитируя щелчок по первому интерактивному элементу. Помимо атрибутов html, элемент управления может быть идентифицирован по его индексу, отсчитываемому от нуля, относительно других вводимых данных внутри формы с помощью атрибута
nr
.dont_click (bool) – Если True, данные формы будут отправлены без нажатия на какой-либо элемент.
Остальные параметры этого метода класса передаются непосредственно методу
FormRequest
__init__
.
Примеры использования Request¶
Использование FormRequest для отправки данных через HTTP POST¶
Если вы хотите смоделировать POST HTML-формы в своём пауке и отправить пару полей «ключ-значение», вы можете возвращает объект FormRequest
(от вашего паука) следующим образом:
return [FormRequest(url="http://www.example.com/post/action",
formdata={'name': 'John Doe', 'age': '27'},
callback=self.after_post)]
Использование FormRequest.from_response() для имитации входа пользователя в систему¶
Обычно веб-сайты предоставляют предварительно заполненные поля формы с помощью элементов <input type="hidden">
, таких как данные, относящиеся к сеансу, или токены аутентификации (для страниц входа). При парсинге вы хотите, чтобы данные поля автоматически заполнялись заранее и переопределяли только некоторые из них, например имя пользователя и пароль. Для этого задания можно использовать метод FormRequest.from_response()
. Вот пример паука, который его использует:
import scrapy
def authentication_failed(response):
# TODO: Check the contents of the response and return True if it failed
# or False if it succeeded.
pass
class LoginSpider(scrapy.Spider):
name = 'example.com'
start_urls = ['http://www.example.com/users/login.php']
def parse(self, response):
return scrapy.FormRequest.from_response(
response,
formdata={'username': 'john', 'password': 'secret'},
callback=self.after_login
)
def after_login(self, response):
if authentication_failed(response):
self.logger.error("Login failed")
return
# continue scraping with authenticated session...
JsonRequest¶
Класс JsonRequest расширяет базовый класс Request
функциями для работы с запросами JSON.
- class scrapy.http.JsonRequest(url[, ... data, dumps_kwargs])¶
Класс
JsonRequest
добавляет два новых ключевых параметров к методу__init__
. Остальные аргументы такие же, как для классаRequest
, и здесь не описаны.Использование
JsonRequest
установит заголовокContent-Type
наapplication/json
и заголовокAccept
наapplication/json, text/javascript, */*; q=0.01
- Параметры
data (object) – is any JSON serializable object that needs to be JSON encoded and assigned to body. if
Request.body
argument is provided this parameter will be ignored. ifRequest.body
argument is not provided and data argument is providedRequest.method
will be set to'POST'
automatically.dumps_kwargs (dict) – Параметры, которые будут переданы базовому методу
json.dumps()
, который используется для сериализации данных в формат JSON.
Пример использования JsonRequest¶
Отправка запроса JSON POST с полезными данными JSON:
data = {
'name1': 'value1',
'name2': 'value2',
}
yield JsonRequest(url='http://www.example.com/post/action', data=data)
Объекты Response¶
Подклассы Response¶
Вот список доступных встроенных подклассов Response. Вы также можете создать подкласс класса Response, чтобы реализовать свои собственные функции.
Объекты TextResponse¶
- class scrapy.http.TextResponse(url[, encoding[, ...]])¶
Объекты
TextResponse
добавляют возможности кодирования к базовому классуResponse
, который предназначен для использования только для двоичных данных, таких как изображения, звуки или любой мультимедийный файл.Объекты
TextResponse
поддерживают новый аргумент метода__init__
в дополнение к базовым объектамResponse
. Остальные функции такие же, как у классаResponse
, и здесь не описаны.- Параметры
encoding (str) – is a string which contains the encoding to use for this response. If you create a
TextResponse
object with a string as body, it will be converted to bytes encoded using this encoding. If encoding isNone
(default), the encoding will be looked up in the response headers and body instead.
Объекты
TextResponse
поддерживают следующие атрибуты в дополнение к стандартнымResponse
:- text¶
Тело ответа в виде строки.
То же, что и
response.body.decode(response.encoding)
, но результат кэшируется после первого вызова, поэтому вы можете обращаться кresponse.text
несколько раз без дополнительных затрат.Примечание
str(response.body)
— неправильный способ преобразовать тело ответа в строку:>>> str(b'body') "b'body'"
- encoding¶
Строка с кодировкой этого ответа. Кодирование разрешается путём использования следующих механизмов по порядку:
кодировка, переданная в аргументе
encoding
метода__init__
кодировка, объявленная в HTTP-заголовке Content-Type. Если эта кодировка недействительна (т.е. неизвестна), она игнорируется и пробуется следующий механизм разрешения.
кодировка, объявленная в теле ответа. Класс TextResponse не предоставляет для этого никаких специальных функций. Однако классы
HtmlResponse
иXmlResponse
работают.кодировка, полученная при просмотре тела ответа. Это более хрупкий метод, но и последний из применявшихся.
- selector¶
Экземпляр
Selector
, использующий ответ в качестве цели. Селектор лениво создаётся при первом доступе.
Объекты
TextResponse
поддерживают следующие методы в дополнение к стандартнымResponse
:- xpath(query)¶
Ярлык на
TextResponse.selector.xpath(query)
:response.xpath('//p')
- css(query)¶
Ярлык на
TextResponse.selector.css(query)
:response.css('p')
Объекты HtmlResponse¶
- class scrapy.http.HtmlResponse(url[, ...])¶
Класс
HtmlResponse
является подклассомTextResponse
, который добавляет поддержку автоматического обнаружения кодировки путём просмотра атрибута HTML meta http-equiv. См.TextResponse.encoding
.
Объекты XmlResponse¶
- class scrapy.http.XmlResponse(url[, ...])¶
Класс
XmlResponse
является подклассомTextResponse
, который добавляет поддержку автоматического обнаружения кодировки путём просмотра строки объявления XML. См.TextResponse.encoding
.