Scarpy源码分析7

本文深入解析了Scrapy框架中Downloader组件的fetch方法,以及它如何通过Middleware进行请求调度和异常处理。重点讲解了DownloaderMiddlewareManager的工作原理,以及slot机制在并发控制中的作用。

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 中移除
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值