嘘~ 正在从服务器偷取页面 . . .

Scrapy之中间件


1. scrapy 启动顺序以及中间件调度顺序


在执行 scrapy crawl * 的指令后爬虫启动顺序如下

  1. start_urls 内部的 url 会交由 start_requests 变为一个生成器

    • 在蜘蛛中间件中可以操作新的请求,响应,item
    • 将会在蜘蛛中间件中提交给引擎。
  2. 之后请求到达引擎,引擎调用调度器分配请求给下载器

    • 在请求到达下载器之前,需要经过下载中间件
    • 经过顺序为:process_request 》发出之后获取网页响应》 process_response
      • 如果在上面两个请求内触发异常,则立即将请求交予process_exception
    • 先经过process_request:当经过这个方法之后请求就已经发出到互联网抓取数据。
    • 当数据回来的时候经过process_response:数据回来的时候最先经过的就是这个方法。
  3. process_response请求完毕返回的时候,这个请求就会交予引擎(印象中是会走引擎的)

  4. 在数据到达回调函数之前还需要经过蜘蛛中间件MidSpiderMiddleware

    1. 这里只会经过process_spider_input 之后进入回调函数
    2. process_spider_output 是所有从回调函数中出来的数据 如item request
    3. 相当于这两个函数 把所有回调函数包起来了
    • 上面中如果回调函数 yield 了 item,那么这个 item 就会到达 process_spider_output

    • 然后在这个方法内通过循环result来将每个 item 交予管道进行处理

      for i in result:
          print('每个item会在这里被处理,每个i就是item')
          yield i

中间件返回内容不同的处理方式


下载中间件(当请求发出,去互联网抓取数据的时候,会经过下载中间件。)

  1. process_request:每个发出去的响应都会经过request方法


    • None:当此方法返回 None,下载将继续处理这个请求,并且将他交予后面的其他下载中间件。

    • response:当此方法返回 response,下载器将立即停止处理这个请求(不会再将请求交予剩下的process_request
    • 并将这个请求交予返回这个响应的中间件process_response方法进行处理

    • request:如果返回一个 request,下载器将立即停止向后继续处理请求,并且重新安排发起返回的新请求。
    • 并且新的请求会重新走一遍中间件调用链

    • raise:当此方法触发了异常,下载器将立即停止处理请求,并将请求交予中间件的process_exception进行处理,
    • 可以指定错误进行指定的处理。
    • 如果没有处理指定异常的函数,那将调用这个请求的 errback(异常回调函数)
    • 如果也没有异常回调函数来处理异常,那么这个异常就会被忽略,并且不记录。

  2. process_response:每个请求完毕回来的响应都会调用这个方法

    • 这个方法必须返回一个 request,或者 response,或者触发异常。
    • response:当此方法返回一个 response,将会把响应继续发送给其他的process_response,直到处理完毕
    • request:当此方法返回一个 request,将立即停止处理,并且将请求重新交予调度器发起全新的请求,会重新走一遍中间件调用链
    • raise:当此方法触发异常,处理方式和上面的一致process_request
  3. 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,   # 第二个触发的下载中间件
}


文章作者: 林木木
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 林木木 !
评论
  目录