作为一个易上手的高性能爬虫框架,Scrapy 使用 Twisted 异步网络框架处理并发请求。
但是,在日常工作和面试过程中,经常发现有些同学会笃定地认为 Scrapy 采用的是多线程并发模型。实际上,虽然 Twisted 框架提供了线程池支持,但是其核心网络部分处理逻辑依赖的是「单线程 IO 多路复用」技术,在 Linux 平台上,是围绕 epoll()
系统调用实现的 Reactor 模式。
为了利用好 Scrapy 的异步任务能力,避免写出 “使用 urllib 和 requests 库完成 HTTP 请求” 这样的错误代码,本文将 Scrapy 各个组件的异步能力及可以使用什么样的异步技术进行一些总结。
可扩展组件:
Spider Middleware - 它是处于 Engine
和 Spider
之间的组件,可以用于处理 Spider
的输入 (response)和输出(item 和 request)。它一般可以用于:处理 Spider 回调函数的输出,可以用于修改、增加和删除 request 或者 item;处理 Spider.start_requests()
函数生成的 request;捕捉 Spider 回调函数抛出的异常等等。用户自己实现的 Spider Middleware 可以定义一个或多个如下方法:
process_spider_input(response, spider)
- 每个响应repsonse
进入 Spider 回调函数之前可由该 方法处理。process_spider_output(response, result, spider)
- Spider 处理完响应response
产生的结果result
可经该方法处理。process_spider_exception(response, exception, spider)
- Spider 回调函数、其它Spider Middleware
的process_spider_input
方法抛出的异常可由该方法处理。process_start_requests(start_requests, spider)
- Spider 启动后由start_requests()
方法产生 的Request
可经方法处理。
Downloader Middleware - 它是处于 Engine
和 Downloader
之间的组件,可以用于处理从 Engine
传递 给 Downloader
的 request 和从 Downloader
传递给 Engine
的 response。它一般可用于:处理即将发到网络上的请求;修改传递即将给 Spider 的响应数据;丢掉响应数据,然后生成一个新的请求;根据请求凭空构造一个响 应(并不发出实际的请求);丢弃某些请求等等。用户自己实现的 Downloader Middleware 可以定义一个或多个如下 方法:
process_request(request, spider)
- 这个方法可以处理每一个经过该中件间的 request。它可以返回None
、Response
实例、Request
实例或者抛出IgnoreRequest
异常。process_responsee(response, spider)
-这个方法可以处理每一个经过该中件间的 response。它可以返回Response
实例、Request
实例或者抛出IgnoreRequest
异常。process_exception(request, exception, spider)
- 这个方法可以处理下载器或者Downloader Middleware
的process_request
抛出的包括IgnoreRequest
在内的所有异常。它可以返回None
、Response
实例 或者Request
实例。
Item Pipeline - 它用于处理 Spider 生成的 item,对其进行清理、验证、持久化等处理。用户自己实现的Item Pipeline 可以定义一个或多个如下方法:
process_item(item, spider)
- 它用来处理 Spider 生成的 item。它可以返回字段类型的数据、Item
实例、Deferred
实例或者抛出DropItem
异常。open_spider(spider)
- Spider 打开时调用。close_spider(spider)
- Spider 关闭时调用。from_crawler(cls, crawler)
Scheduler - Scheduler接收来自engine的请求,并在engine请求它们时将它们排入队列以便稍后(也引导到engine)。
Extension - 提供了向 Scrapy 中插入自定义功能的机制。Extension 是普通的类,它们在 Scrapy 启动时实例化。 通常,Extension 实现向 Scrapy 注册信号处理函数,由信号触发完成相应工作。
Spider - Spiders是由Scrapy用户编写的自定义类,用于解析响应并从中提取items(也称为下载的items)或其他要跟进的requests。
异步手段
Twisted Deferred
我们本节主要汇总一下 Scrapy 中哪些可扩展组件支持返回 Deferred
对象。
Item Pipeline
对于 Item Pipeline,我们从文档中已经得知,用户自定义 Item Pipeline 的 process_item
可以返回 Deferred
实例。Item
在 pipeline 的处理本身就是由 Deferred
驱动的,作为其回调函数使用的 process_item
返回的 Deferred
便会插入到原始 Deferred
的处理流程中。
# scrapy.core.scraper.Scraper
def _process_spidermw_output(self, output, request, response, spider):
"""Process each Request/Item (given in the output parameter) returned
from the given spider
"""
if isinstance(output, Request):
self.crawler.engine.crawl(request=output, spider=spider)
elif isinstance(output, (BaseItem, dict)):
self.slot.itemproc_size += 1
dfd = self.itemproc.process_item(output, spider)
dfd.addBoth(self._itemproc_finished, output, response, spider)
return dfd
elif output is None:
pass
else:
###