IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Scrapy源码学习-Middleware -> 正文阅读

[网络协议]Scrapy源码学习-Middleware

使用scrapy框架开发一款爬虫,或多或少都会用到中间件。常见的中间件有UserAgent中间件、代理中间件等等。其主要作用就是在爬虫请求的过程中,通过自定义内置的一些方法,来改变如请求的结构,从而伪装请求客户端。
比如,在UserAgent中间件中,通过编写process_request方法,将请求中的headers添加自定义ua,或者随机ua来实现动态UserAgent。

class UserAgentMiddleware:

    def process_request(self, request, spider):
        request.headers.setdefault('User-Agent', random_ua())

当然,仅仅编写自定义中间件是不够的。还需要在配置文件中,开启该中间件。

DOWNLOADER_MIDDLEWARES = {
    ……,
    'SpiderPorject.middlewares.UserAgentMiddleware': 1
}

通过两步走:1.编写中间件;2.开启中间件。即可实现简单的中间件开发。框架开发者已经足够精简业务编写的流程。接下来抱着学习的态度,看看到底是如何实现的。

MiddlewareManager

MiddlewareManager是所有中间件的基类,所有中间件指的是:

  • DownloaderMiddlewareManager:下载中间件
  • SpiderMiddlewareManager:爬虫中间件
  • ExtensionManager:扩展中间件
  • ItemPipelineManager:数据管道中间件

我们先从最初的父类_MiddlewareManager_来看。
该类有一个静态方法from_settings来初始化这个类,在里面主要做一件事,就是获取配置文件中的中间件path。何为中间件path?看看上面,我们是如何启用UserAgent中间件的。在DOWNLOADER_MIDDLEWARESSpiderPorject.middlewares.UserAgentMiddleware就是这个中间件的路径。通过方法load_object把路径映射成类,并通过create_instance实例化该类,存入数组middlewares中。最后把middlewares交给_MiddlewareManager_。然后类在初始化的时候遍历middlewares调用方法_add_middleware

class MiddlewareManager:

    def __init__(self, *middlewares):
        self.middlewares = middlewares
        self.methods = defaultdict(deque)
        for mw in middlewares:
            self._add_middleware(mw)

    @classmethod
    def from_settings(cls, settings, crawler=None):
        mwlist = cls._get_mwlist_from_settings(settings)
        middlewares = []
        enabled = []
        for clspath in mwlist:
            try:
                mwcls = load_object(clspath)
                mw = create_instance(mwcls, settings, crawler)
                middlewares.append(mw)
                enabled.append(clspath)
            except NotConfigured as e:
               ……
        return cls(*middlewares)

_add_middleware在父类默认做了哪些事?它就是在中间件类中把open_spiderclose_spider(如有)放到methods里。

    def _add_middleware(self, mw):
        if hasattr(mw, 'open_spider'):
            self.methods['open_spider'].append(mw.open_spider)
        if hasattr(mw, 'close_spider'):
            self.methods['close_spider'].appendleft(mw.close_spider)

其他的中间件只需要基于父类,通过重写部分方法,即可达到其目的。

DownloaderMiddlewareManager

有了上面的经验,再看_DownloaderMiddlewareManager_就比较清晰了。它主要就是重写了父类的_get_mwlist_from_settings(从配置项中获取中间件路径),由于它是下载中间件,所以它只需要拿到DOWNLOADER_MIDDLEWARES中配置的中间件路径。
继续重写父类的_add_middleware,拿到自定义中间件中process_requestprocess_responseprocess_exception(如有)加入到methods中。通过download方法的Deferred机制触发methods中需要执行的方法,包括方法回调,方法异常触发。

    def download(self, download_func, request, spider):
        @defer.inlineCallbacks
        def process_request(request):
            ……
            return (yield download_func(request=request, spider=spider))

        @defer.inlineCallbacks
        def process_response(response):
            ……
            return response

        @defer.inlineCallbacks
        def process_exception(failure):
            ……
            return failure

        deferred = mustbe_deferred(process_request, request)
        deferred.addErrback(process_exception)
        deferred.addCallback(process_response)
        return deferred

download又是通过谁来执行的呢?熟悉的朋友都知道scrapy的一大组件叫下载器Downloader,在默认配置文件default_settings文件中:

DOWNLOADER = 'scrapy.core.downloader.Downloader'

走进这个执行文件中观察,这个_Downloader_类在初始化时候同样声明了DownloaderMiddlewareManager,并有一个叫fetch的方法,调用了middleware.download()

class Downloader:

    def __init__(self, crawler):
        ……
        self.middleware = DownloaderMiddlewareManager.from_crawler(crawler)

    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)
SpiderMiddlewareManager

再看SpiderMiddlewareManager就更容易了,_get_mwlist_from_settings从配置项中拿到属性为SPIDER_MIDDLEWARES的中间件路径。_add_middleware提取自定义Spider中间件中的process_spider_inputprocess_start_requestsprocess_spider_outputprocess_spider_exception(如有)加入methods中。外部引擎(ExecutionEngine)通过调用scrape_response来执行工作流。

class SpiderMiddlewareManager(MiddlewareManager):

    component_name = 'spider middleware'

    @classmethod
    def _get_mwlist_from_settings(cls, settings):
        return build_component_list(settings.getwithbase('SPIDER_MIDDLEWARES'))

    def _add_middleware(self, mw):
        super()._add_middleware(mw)
        if hasattr(mw, 'process_spider_input'):
            self.methods['process_spider_input'].append(mw.process_spider_input)
        if hasattr(mw, 'process_start_requests'):
            self.methods['process_start_requests'].appendleft(mw.process_start_requests)
        process_spider_output = getattr(mw, 'process_spider_output', None)
        self.methods['process_spider_output'].appendleft(process_spider_output)
        process_spider_exception = getattr(mw, 'process_spider_exception', None)
        self.methods['process_spider_exception'].appendleft(process_spider_exception)

    def scrape_response(self, scrape_func, response, request, spider):

        def process_spider_input(response):
            ……
            return scrape_func(response, request, spider)

        def process_spider_exception(_failure, start_index=0):
            ……
            return _failure

        def process_spider_output(result, start_index=0):
            ……
            return MutableChain(result, recovered)

        def process_callback_output(result):
            ……
            return MutableChain(process_spider_output(result), recovered)

        dfd = mustbe_deferred(process_spider_input, response)
        dfd.addCallbacks(callback=process_callback_output, errback=process_spider_exception)
        return dfd

ExtensionManager

扩展,它其实也是个中间件。扩展的应用场景一般都是在爬虫启动和关闭的时候绑定信号量,触发对应的事件。
它的类实现及其简单:

class ExtensionManager(MiddlewareManager):
    @classmethod
    def _get_mwlist_from_settings(cls, settings):
        return build_component_list(settings.getwithbase('EXTENSIONS'))

仅仅就是重写扩展中间件获取方式。
尽管这样,他一样拥有了父类自带的open_spiderclose_spider。比如你可以在自定义扩展中,使用open_spider来open一个资源,在close_spider的时候销毁。

ItemPipelineManager

数据管道,它其实也是个中间件!它在重写_add_middleware的时候先运行了父类的_add_middleware(也就是增加了open_spiderclose_spider方法),再增加了process_item方法。

class ItemPipelineManager(MiddlewareManager):
    @classmethod
    def _get_mwlist_from_settings(cls, settings):
        return build_component_list(settings.getwithbase('ITEM_PIPELINES'))

    def _add_middleware(self, pipe):
        super(ItemPipelineManager, self)._add_middleware(pipe)
        if hasattr(pipe, 'process_item'):
            self.methods['process_item'].append(deferred_f_from_coro_f(pipe.process_item))

所以,往往我们在自己定义ItemPipeline的时候,必须实现方法process_item来拿到我们抓取的数据。

总结

Middleware

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-02-16 13:30:42  更:2022-02-16 13:32:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 10:00:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码