1. scrapy 启动顺序以及中间件调度顺序
在执行 scrapy crawl * 的指令后爬虫启动顺序如下
start_urls 内部的 url 会交由 start_requests 变为一个生成器
- 在蜘蛛中间件中可以操作新的请求,响应,item
- 将会在蜘蛛中间件中提交给引擎。
之后请求到达引擎,引擎调用调度器分配请求给下载器
- 在请求到达下载器之前,需要经过下载中间件
- 经过顺序为:process_request 》发出之后获取网页响应》 process_response 》
- 如果在上面两个请求内触发异常,则立即将请求交予process_exception
- 先经过process_request:当经过这个方法之后请求就已经发出到互联网抓取数据。
- 当数据回来的时候经过process_response:数据回来的时候最先经过的就是这个方法。
当process_response请求完毕返回的时候,这个请求就会交予引擎(印象中是会走引擎的)
在数据到达回调函数之前还需要经过蜘蛛中间件MidSpiderMiddleware
- 这里只会经过
process_spider_input
之后进入回调函数- 而
process_spider_output
是所有从回调函数中出来的数据 如item
request
- 相当于这两个函数 把所有回调函数包起来了
上面中如果回调函数 yield 了 item,那么这个 item 就会到达 process_spider_output
然后在这个方法内通过循环result来将每个 item 交予管道进行处理
for i in result: print('每个item会在这里被处理,每个i就是item') yield i
中间件返回内容不同的处理方式
下载中间件(当请求发出,去互联网抓取数据的时候,会经过下载中间件。)
process_request:每个发出去的响应都会经过request方法
- None:当此方法返回 None,下载将继续处理这个请求,并且将他交予后面的其他下载中间件。
- response:当此方法返回 response,下载器将立即停止处理这个请求(不会再将请求交予剩下的process_request)
- 并将这个请求交予返回这个响应的中间件的process_response方法进行处理
- request:如果返回一个 request,下载器将立即停止向后继续处理请求,并且重新安排发起返回的新请求。
- 并且新的请求会重新走一遍中间件调用链
- raise:当此方法触发了异常,下载器将立即停止处理请求,并将请求交予中间件的process_exception进行处理,
- 可以指定错误进行指定的处理。
- 如果没有处理指定异常的函数,那将调用这个请求的 errback(异常回调函数)
- 如果也没有异常回调函数来处理异常,那么这个异常就会被忽略,并且不记录。
process_response:每个请求完毕回来的响应都会调用这个方法
- 这个方法必须返回一个 request,或者 response,或者触发异常。
- response:当此方法返回一个 response,将会把响应继续发送给其他的process_response,直到处理完毕
- request:当此方法返回一个 request,将立即停止处理,并且将请求重新交予调度器发起全新的请求,会重新走一遍中间件调用链
- raise:当此方法触发异常,处理方式和上面的一致process_request
process_exception:当下载器中的请求或者响应触发异常时候,此方法被调用
- None:当返回 None,中间件将继续处理这个请求,并且交予剩下的process_exception中间件进行处理
- response:当返回 response,则立即停止继续调用其他 exception,启动已安装的响应处理中间件进行处理(相当于刚从互联网请求回来的调用链)
- request:当返回 request 的时候,则立即停止继续调用其他 exception,重新交予调度器安排发起新的请求
爬虫中间件
- 可以在其中插入自定义功能来处理发送给 Spider 进行处理的 响应 以及处理从蜘蛛生成的 请求 和 item
- 当 Response 交给回调函数之前 会经过爬虫中间件
process_spider_input
- 当 回调函数 生成新的请求 发给调度器安排新的请求之前 会经过爬虫中间件
process_spider_output
- 当 item 在提交给管道之前 会经过爬虫中间件
process_spider_output
- 爬虫中间件比较核心,请求,响应,item,都会经过,Scrapy 启动时候的请求,也是这里发出.
- 爬虫回调的后处理输出 - 更改/添加/删除请求或项目;
- 后处理 start_requests;
- 处理爬虫异常;
- 根据响应内容为某些请求调用 errback 而不是回调。
process_spider_input:每个从互联网回来的数据都会经过这个方法
- None:如果返回 None,将继续处理这个响应,并且交予其他中间件,直到最终交给 parse(回调函数)
- raise:如果触发异常,将不会调用其他 spider_input,如果存在 errback(异常回调函数)将会调用异常回调函数
- 否则启动process_spider_exception链
- 如果进入了 errback(异常回调函数):那么异常回调函数的输出将会到达process_spider_output。
- 因为这两个函数 就是将回调函数完全包裹控制起来了
- 如果内部触发异常,将再次回到process_spider_exception链
process_spider_output:从回调函数中返回的
item
request
都会先经过这个方法
- None:返回 None,将继续处理这个响应
- item:这个方法接收一个参数result,这个参数是一个可迭代对象,里面每个对象就是在回调函数中 yield 的 item 或请求
- request:如果返回一个 request 对象,那么将请求重新交予调度器进行发起,之后按正常的流程走一遍
- 注意:这里流程走了还是会继续向后传递 item,所以需要判断一下。
process_spider_exception:当爬虫中间件触发异常的时候调用此方法
此方法应该返回 None,request,item 中的一个
None:当返回 None,继续处理这个异常,流程和上面的异常一致
request:当返回 request,将立即结束异常处理,并且将请求重新交予调度器发起全新的请求,并且会重新走一遍调度链
item:返回 item 在官网的解释是
- 如果它返回一个可迭代的 process_spider_output()管道,则从下一个蜘蛛中间件开始,不会 process_spider_exception()调用其他中间件
讲的太绕了,应该是返回 item 就会提交到 item 中间件里面,不会继续调用异常处理程序了。
def process_start_requests(self, start_requests: Generator, spider: Spider): # 通过蜘蛛的启动信号进行调用,并工作. # 循环 start_requests 返回的生成器,将每个请求发给引擎进行处理. # 必须返回请求,而不是item. # 从启动到开始只会调用一次. for r in start_requests: yield r
# 爬虫中间件: # 当下载请求完数据返回的时候会经过的一个中间件 # 优先级越低,离引擎越远,也就是说,数据回来最先经过的就是数值最低的 # 可以理解为从外部回来,最需要经过层层过滤,所以最先经过的就是数值最低的(最外层) # 这里经过完毕后会返回到回调函数 SPIDER_MIDDLEWARES = { 'spider.middlewares.SpiderSpiderMiddleware': 542, # 第一个触发的爬虫中间件 'spider.middlewares.SpiderSpiderMiddleware2':543, # 第二个触发的爬虫中间件 } # Enable or disable downloader middlewares # See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html # 下载中间件: # 当请求从引擎发出,去互联网拿取数据的的时候会触发的一个中间件 # 中间件的优先级是 数值越小,离引擎越近。 # 因为离引擎近,所以在请求出去的时候会被立即触发 # 可以理解为从内部出去,需要经过层层处理,所以数值小的先处理 # 下面的中间件请求就是先触发542,再触发543 DOWNLOADER_MIDDLEWARES = { 'spider.middlewares.SpiderDownloaderMiddleware': 542, # 第一个触发的下载中间件 'spider.middlewares.SpiderDownloaderMiddleware2': 543, # 第二个触发的下载中间件 }