2021SC@SDUSC
3.4 Downloader
看下 fetch 方法
# scrapy/core/downloader/__init__.py
class Downloader(object):
# ...
def fetch(self, request, spider):
def _deactivate(response):
self.active.remove(request)
return response
self.active.add(request)
dfd = self.middleware.download(self._enqueue_request, request, spider)
return dfd.addBoth(_deactivate)
- 首先还是将请求加入 downloader 的活跃集合 active
- 然后调用自己的 DownloaderMiddlewareManager 实例的 download 方法触发
_enqueue_request下载请求 - 下载完成后,将请求从 active 中移除
在看方法 _enqueue_request 之前,我们先看下 download 的定义
# scrapy/core/downloader/middleware.py
class DownloaderMiddlewareManager(MiddlewareManager):
# ...
def download(self, download_func, request, spider):
@defer.inlineCallbacks
def process_request(request):
for method in self.methods['process_request']:
response = yield method(request=request, spider=spider)
if response is not None and not isinstance(response, (Response, Request)):
raise _InvalidOutput('Middleware %s.process_request must return None, Response or Request, got %s' % \
(six.get_method_self(method).__class__.__name__, response.__class__.__name__))
if response:
defer.returnValue(response)
defer.returnValue((yield download_func(request=request, spider=spider)))
@defer.inlineCallbacks
def process_response(response):
# ...
@defer.inlineCallbacks
def process_exception(_failure):
# ...
deferred = mustbe_deferred(process_request, request)
deferred.addErrback(process_exception)
deferred.addCallback(process_response)
return deferred
- 首先触发
process_request,调用所有已注册的下载中间件中的process_request方法,然后调用_enqueue_request - 下载成功后,执行
process_exception - 下载失败后,执行
process_response
如果之前写过下载器中间件,你会发现你这里的 3 个以 process_ 开头的方法和中间件一一对应。的确,这里就是调度下载器中间件的底层实现。
回到 _enqueue_request
# scrapy/core/downloader/__init__.py
class Downloader(object):
# ...
def _enqueue_request(self, request, spider):
key, slot = self._get_slot(request, spider)
request.meta[self.DOWNLOAD_SLOT] = key
def _deactivate(response):
slot.active.remove(request)
return response
slot.active.add(request)
self.signals.send_catch_log(signal=signals.request_reached_downloader,
request=request,
spider=spider)
deferred = defer.Deferred().addBoth(_deactivate)
slot.queue.append((request, deferred))
self._process_queue(spider, slot)
return deferred
和 engine 类似,downloader 里面也有 slot 的概念,不过 1 个 downloader 有多个 slot。不同的 slot 主要是用于控制不同的并发、延迟下载,同时也会记录下载中的请求。
做了这么几件事
- 根据请求获取 slot,并将当前请求加入到对应 slot 的活跃集合 active 中
- 发送
request_reached_downloader信号 - 将请求加入下载队列,调用
_process_queue会触发下载方法,下载完成后又触发_deactivate将当前请求从 slot 的 active 中移除
本文深入解析了Scrapy框架中Downloader组件的fetch方法,以及它如何通过Middleware进行请求调度和异常处理。重点讲解了DownloaderMiddlewareManager的工作原理,以及slot机制在并发控制中的作用。
9128

被折叠的 条评论
为什么被折叠?



