本文为译文,原文见地址:https://docs.scrapy.org/en/latest/topics/request-response.html
Scrapy使用Request和Response对象来爬行web站点。
通常来说,Request对象在爬虫中生成,并且系统中传递,直到它们到达下载器(Downloader),下载器执行请求并返回Response对象,Response对象返回到发出请求的爬虫。
Request和Response类都可以有子类,这些子类添加了基类中不是必须的功能。后续会有介绍。
class scrapy.http.Request(url[, callback, method=‘GET’, headers, body, cookie, meta, encoding=‘utf-8’, priority=0, dont_filter=False, errback, flags])
一个Request对象代表一个HTTP请求,Request对象通常在爬虫(Spider)中生成,并且由下载器(Downloader)执行,最终的结果生成一个Response对象。
参数:
url(string)- 该请求的url
callback(callable)- 回调函数,调用这个函数时的第一个参数,是该请求的响应(下载后)。想要获取更多信息,请见后面的传递附加数据到回调函数。如果一个Request没有指定callback,那么将默认使用爬虫的parse()函数。注意,如果在处理过程中出现了异常,那么将会调用errback而不是callback。
method(string)- 该请求的HTTP函数。默认为’GET’。
meta(dict)- Request.meta属性的初始值。如果给定了该值,该参数传递的字典将被浅拷贝。
body(str或者unicode)- 请求的body。如果传递了一个unicode,那么将使用传递的encoding参数(默认为utf-8)来将其转码为str。如果body没有给定,将存储一个空字符串。不论这个参数的类似那个是什么,最终存储的都将是一个str(不管unicode或是None)。
header(dict)- 该请求的头。这个字典的值可以是字符串(单独值的头)或者列表(多个值的头)。如果传递了None作为值,HTTP头将根本不会被发送。
cookies(dict或者list)- 请求的cookies。可以是以下两种格式。
使用字典:
request_with_cookies = Request(url='http://www.example.com', cookies={'currency': 'USD', 'country': 'UY'})
使用字典的列表:
request_with_cookies = Request(url='http://www.example.com', cookies=[{'name': 'currency', 'value': 'USD', 'domain': 'example.com', 'path': '/currency'}])
后一种形式允许自定义cookie的domain和path属性。这只有在cookie被保存,以备后续的请求可以使用。
当一些站点返回了cookies(在响应中),这些cookie将被存储在domain的cookie中,并且后续的请求中都需要带有这些cookie。这是任何常用的web浏览器都带有的典型行为。然而,如果为了某些原因,你希望避免合并已经存在的cookie,你可以在Request.meta中设置dont_merge_cookies关键字为True来指示Scrapy这样做。
请求不带合并cookie,示例如下:
request_with_cookie = Request(url='http://www.example.com', cookies={'currency': 'USD', 'country': 'UY'}, meta={'dont_merge_cookies': True})
更多信息请见CookiesMiddleware。
url
一个包含了该请求的URL字符串。请记住,这个属性包含的是转义后的URL,因此它可能会与传递给构造函数中的URL不同。
这个属性是一个只读属性。若要改变请求的URL,请使用replace()。
method
在请求中表示HTTP函数的字符串。它保证是大写的。比如:“GET”,“POST”,“PUT”,等。
headers
一个类似字典的对象,包含了请求的头。
body
包含了请求正文的字符串。
这个属性是只读的。若要改变请求正文,请使用replace()。
meta
一个包含了该请求任意元数据的字典。对于新请求来说这个字典是空的,并且这个字典通常被不同的Scrapy组件所填充(扩展,如中间件,等)。因此这个字典所包含的内容,是根据你已经启用的扩展来决定的。
Scrapy可识别的特定meta键,请参见Request.meta的特定键。
当请求通过copy()或者replace()函数来克隆的时候,这个字典是被浅拷贝的,并且依然能够被访问——在你的爬虫中,访问response.meta属性即可。
copy()
返回一个新请求,这个请求是该请求的复制品。请参见:传递附加数据到回调函数。
replace([url, method, headers, body, cookies, meta, encoding, dont_filter, callback, errback])
返回一个请求对象,这个请求对象有用相同的成员,除非指定关键字参数给定了新的值。属性Reqeust.meta默认是拷贝的(除非meta参数给定了新的值)。请参见:传递附加数据到回调函数。
请求的callback是一个函数,当该请求的响应被下载的时候调用此回调函数。该回调函数的第一个参数即为下载的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):
# 将记录http://www.example.com/some_page.html
self.logger.info('Visited %s', response.url)
在某些情况下,你可能会对传递给这些回调函数的参数感兴趣,因此你可以晚一点接收这些参数,比如在第二个回调函数中。你可以使用Request.meta属性来实现。
这里有一个示例演示了如何通过传递一个item来使用这个机制,主要是为了在不同页面填充不同的字段:
der parse_page1(self, response):
item = MyItem()
item['main_url'] = response.url
request = scrapy.Request('http://www.example.com/some_page.html', callback=self.parse_page2)
request.meta['item'] = item
yield request
def parse_page2(self, response):
item = response.meta['item']
item['other_url'] = response.url
yield item
请求的errback是一个函数,在处理请求的过程中如果抛出了一个异常则会调回这个errback函数。
errback将接收Twisted 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
'http://www.httphttpbinbin.org/', # 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 sucessful response from {}'.format(response.url))
# 剩余代码...
def errback_httpbin(self, failure):
# 记录所有错误
self.logger.error(repr(failure))
# 如果你想要处理某些特定的错误,你需要以failure的type为依据:
if failure.check(HttpError):
# 这些异常来自爬虫中间件的HttpError
# 你可以获取到所有非200的响应
response = failure.value.response
self.logger.error('HttpError on %s', response.url)
elif failure.check(DNSLookupError):
# 这是原始请求
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)
Request.meta属性可以包含任意数据,但是这里有一些Scrapy以及其内置扩展能够识别的特定键:
外网IP地址,用来执行请求。
下载器等待的最大时间,单位秒。也可见配置项的DOWNLOAD_TIMEOUT。
自请求开始以来,用于获取响应花费的时间总量(单位秒),比如通过网络发送HTTP消息。只有当响应被下载后,这个元数据键才可用。虽然大多数元数据键用于控制Scrapy的行为,但是这个键只能是只读的。
是否在中断的响应上失败。也可见配置项的DOWNLOAD_FAIL_ON_DATALOSS。
这个元数据键用来设置每个请求的重试次数。当初始化后,元数据键max_retry_times的优先级将高于配置项RETRY_TIMES。
这里有一组内置的Request子类。你也可以继承Request类来实现你所需的功能。
FormRequest类扩展了基础类Request的处理HTML表单的功能。FormRequest类使用了lxml.html forms,将Response对象的表单数据中预填充到表单字段中。
class scrapy.http.FormRequest(url[, formdata, …])
FormRequest类在构造函数中添加了新的参数。剩余的参数与Request类保持一致,这里就不再赘述。
参数:
FormRequest对象在Request基础上,附加了一些类函数:
classmethod from_response(response[, formname=None, formid=None, formnumber=0, formdata=None, formxpath=None, formcss=None, clickdata=None, dont_click=False, … ])
返回一个FormRequest对象,该对象的表单字段预先填充了响应中包含的HTML