为什么需要从request传数据到response
请求传数据给响应的适用情况其实有挺多,举个简单的分页请求的场景:
页面参数中有一个分页参数page,取值是1,2,3,4……。但是我们不知道总页数是多少,所以不可以在start_requests方法中循环遍历。只能在parse方法中接收响应数据来判断是否要请求下一页。如果我们认为还有下一页(比如当前页不为空),那么我们拿到当前页的page参数+1就可以得到下一页的页数。一种方便的方法是发请求的时候就共享page参数给个parse方法,但是因为scrapy的异步架构,我们不能用一个全局变量来实现共享,否则会出现很多问题,这时候我们就可以在发请求的时候把当前页数作为一个参数传给响应接收者parse方法。
示例程序
这段代码请求了abcd四个类型下的每一个分页,并根据抓取到的数据是否为空决定是否请求下一页。
在scrapy.Request请求方法中,我们用cb_kwargs来向接收响应的parse方法传参。在parse方法中,我们需要相应的加多两个参数来接收cb_kwargs共享的参数。
import scrapy import json import requests from wecom.items import DepartmentItem, InternalUserItem class ExampleSpider(scrapy.Spider): name = 'example' def start_requests(self): for type in ['a', 'b', 'c', 'd']: page = 1 yield scrapy.Request(url="https://example.com/list?type=%s&page=%d" % (type, page), cb_kwargs = {'type': type,'page':page}) def parse(self, response, type, page): list = response.css('li').getall() if len(list) == 0: return for item in list: yield item yield scrapy.Request(url="https://example.com/list?type=%s&page=%d" % (t, page+1), cb_kwargs = {'type': type,'page':page+1})
可以用共享变量吗
看上面的程序我们也很容易想到一种方式是把type,page这两个请求参数作为类成员变量,那么在这个类的所有成员方法都是可以共享的。但是scrapy的response是异步回调的,所以用共享变量会出问题。
我们看看scrapy的架构,下面这张图上有8个流程组成的流水线。假设我们发出了两个请求,第二个请求会在第一个请求发出(第①步)后马上开始,而不是等第一个请求的第⑧步完成。所以当第一个请求的response(第⑥步)返回的时候,全局变量已经不知道变成了什么。
其他参数共享方法
其实在response对象中我们可以用response.request
来访问到对应的request对象的,但是我们如果通过request解析参数,会多一些麻烦的步骤,用共享参数的方式会更优雅。另外,如果要共享一些不属于request参数的数据就只能用cb_kwargs了。