Spider_Man_6 の Scrapy_Downloader Middleware(这是个需要针对一下的东西🐷🐷🐷)

Posted ugfly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spider_Man_6 の Scrapy_Downloader Middleware(这是个需要针对一下的东西🐷🐷🐷)相关的知识,希望对你有一定的参考价值。

下载器中间件(Downloader Middleware)

  下载器中间件是介于Scrapy的request/response处理的钩子框架。是用于全局修改Scrapy request和response的一个轻量、底层的系统。

  它处于 引擎(crawler.engine) 和 下载器(crawler.engine.download())之间的一层组件,支持多个下载中间件被加载运行。

  我们要这个骚东西有何用?

技术分享图片
首先不能否定的是,可以干。
1. 当引擎传递请求给下载器的过程中,下载中间件可以对请求进行处理(例如增加http header信息,增加proxy信息等);

    2. 当下载器完成http请求,传递响应给引擎的过程中,下载中间件可以对响应进行处理(例如进行gzip的解压等)。
    
    
        关于自定义中间件的意义:
            1. 在process_request内,自定义下载不用scrapy的下载。
            2. 对请求进行二次加工,例如:
                    设置请求头
                    设置cookie
                    添加代理:scrapy自带的代理组件
                          from scrapy.downloadermiddlewares.httpproxy import HttpProxyMiddleware
                    from urllib.request import getproxies        
下载器中间件你到底能干嘛?

 

激活下载器中间件:

  要激活下载器中间件,将其加入到DOWNLOAD设置中。该设置是一个字典(dict),键为中间件类的路径,值为其中间件的顺序(order)。

  看例子:在你的settings文件里面

DOWNLOADER_MIDDLEWARES = {
   Baidu.middlewares.BaiduDownloaderMiddleware: 543,
}

注意了宝宝们,downloader_middlewares设置会与Scrapy定义的downloader_middlewares_base设置合并(非覆盖),然后根据顺序(order)进行怕徐,最后得到启用中间件的有序列表:第一个中间件是最靠近引擎的,最后一个是最靠近下载器的。

附:

技术分享图片
DOWNLOADER_MIDDLEWARES_BASE = {
    # Engine side
    scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware: 100,
    scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware: 300,
    scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware: 350,
    scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware: 400,
    scrapy.downloadermiddlewares.useragent.UserAgentMiddleware: 500,
    scrapy.downloadermiddlewares.retry.RetryMiddleware: 550,
    scrapy.downloadermiddlewares.ajaxcrawl.AjaxCrawlMiddleware: 560,
    scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware: 580,
    scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware: 590,
    scrapy.downloadermiddlewares.redirect.RedirectMiddleware: 600,
    scrapy.downloadermiddlewares.cookies.CookiesMiddleware: 700,
    scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware: 750,
    scrapy.downloadermiddlewares.stats.DownloaderStats: 850,
    scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware: 900,
    # Downloader side
}
DOWNLOADER_MIDDLEWARES_BASE

补充:

  如果你想充分利用好你的中间件,请注意一下中间件顺序

技术分享图片
在setting中,可以自定义中间件,接受各种request、response、 exception消息
比如有的人想在请求超时时 做一些处理,
有的人想为request设置代理

DOWNLOADER_MIDDLEWARES = {
discountSpider.middlewares.ProcessMiddleware:90,    
discountSpider.middlewares.ProxyMiddleware: 750, 
scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware: 751, 
discountSpider.middlewares.RandomUserAgent: 400,
scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware: None,
}
然而,中间件的顺序是很重要的
如果是想截取process_request,则越前面越早收到通知,然后顺着向后依次通知,没什么意外的话是这样。
但是如果是截取process_response,获得request请求完成后,返回response的消息,如果是正常下载完成的话,是第一个中间件收到通知,然后顺序向后通知。
但是往往会发生很多意外,比如请求超时,比如请求被retry,那么是不会发送通知到第一个的,而是根据scrapy默认中间件的位置发送,
分配中间件的顺序请查看 DOWNLOADER_MIDDLEWARES_BASE 设置,而后根据您想要放置中间件的位置选择一个值。由于每个中间件执行不同的动作,您的中间件可能会依赖于之前(或者之后)执行的中间件,因此顺序是很重要的。

比如DOWNLOADER_MIDDLEWARES_BASE中
scrapy.contrib.downloadermiddleware.retry.RetryMiddleware: 500, 如果你把自定义的中间件序号设为100,那么当下载器发生错误504 500错误,需要retry,则会将response状态为retry的消息通知后面中间件,你的中间件的process_response将无法收到消息,因为他是从500序号开始向后通知,所需你将自己的中间件序号改为500以后,则可以收到消息。

又比如
scrapy.contrib.downloadermiddleware.downloadtimeout.DownloadTimeoutMiddleware: 350,如果你想收到下载页面超时的消息,请将中间件放到350以后,process_response才能收到消息。因为下载发生超时后,scrapy会直接找到scrapy.contrib.downloadermiddleware.downloadtimeout.DownloadTimeoutMiddleware模块的位置350,通知他,然后消息依次通知后面的比如400,500,550,700等位置的中间件。如果你的中间件位置在100之类的,那是收不到消息的。

参考链接:https://www.jianshu.com/p/3b7507e7fc65
中间件注意事项

 

关于如何分配中间件的顺序请查看 DOWNLOADER_MIDDLEWARES_BASE 设置,而后根据您想要放置中间件的位置选择一个值。由于每个中间件执行不同的动作,您的中间件可能会依赖于之前(或者之后)执行的中间件,因此顺序是很重要的。

如果您想禁止内置的(在 DOWNLOADER_MIDDLEWARES_BASE 中设置并默认启用的)中间件,您必须在项目的 DOWNLOADER_MIDDLEWARES 设置中定义该中间件,并将其值赋为 None。例如,如果您想要关闭 user-agent 中间件:

DOWNLOADER_MIDDLEWARES = {
    myproject.middlewares.CustomDownloaderMiddleware: 543,
    scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware: None,
}   # 这里有疑问,我直接注掉和赋值为None,区别在哪?

最后,请注意,有些中间件需要通过特定的设置来启用。更多内容请查看相关中间件文档。

 

好了,既然我们已经学会了激活它,现在就到此为止吧。你已经可以下山了!!!

??

??

??

??

??

??

开个玩笑,别当真。

其实自定义一个属于我们自己的下载器中间件不难,甚至有点简单。

每个中间件组件是一个定义了一个或者多个方法的python类。

从三个点去突破。

process_request(request,spider):每个request通过下载中间件是,该方法会被调用。并且该方法,算了。一会在代码里详细写注释吧。??

还是说了吧,须返回:None、Response对象、Request对象、raise IgnoreRequest其中之一!!!

process_response:代码里见

process_exception:捕捉异常用。

看码:

class BaiduDownloaderMiddleware(object):
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the downloader middleware does not modify the
    # passed objects.

    @classmethod
    def from_crawler(cls, crawler):
        """
        # This method is used by Scrapy to create your spiders.
        scrapy中的许多类都实现了该方法
        通过调用该方法来生成下载器中间件管理器对象
        那再补充两句:
            每一个spider对应一个crawler,一个crawler会使用一个engine
            一个CrawlerProcess中可以创建多个crawler进行多种爬取。
        """
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_request(self, request, spider):
        # Called for each request that goes through the downloader
        # middleware.

        # Must either:
        # - return None: continue processing this request
        # - or return a Response object
        # - or return a Request object
        # - or raise IgnoreRequest: process_exception() methods of
        #   installed downloader middleware will be called
        """
        当每个request通过下载器中间件时,process_request被调用
        :param request:  要处理的request
        :param spider:   该request对应的spider
        :return:
            必须返回四者其一!!!
            None--> scrapy继续处理该request,会执行其他中间件的相应方法。
                    直到合适的下载器处理函数(download handler)被调用该request被执行,该request被执行(其response被下载)。
            Request对象--> Scrapy停止调用 process_request()方法并重新调度返回的request。当新返回的request被执行后, 相应地中间件链将会根据下载的response被调用。
            Response对象--> 已经安装的中间件的process_response()方法会在每个response返回时调用。
            raise IgnoreRequest异常--> 停止process_request的执行,开始执行process_exception
        """
        return None

    def process_response(self, request, response, spider):
        """
        当下载器完成http请求,传递响应给引擎的时候调用。
        :param request:  发起响应的请求
        :param response: 正在处理的响应
        :param spider:   这个响应所对应的spider
        :return:
            如果返回一个reponse对象(可以与传入的reponse相同,也可以是一个全新的对象),那么响应将继续与process_response()链中的下一个中间件一起处理。
            如果返回一个request对象,那么中间件链将暂停,并且返回的请求将被重新安排在将来被下载。这与返回请求的行为是一样的process_request()。
            如果raise一个IgnoreRequest异常,则调用request的errback(Request.errback)。
            如果没有代码处理抛出的异常,则在下面爬虫中间件会说这个东西。配合着process_spider_input()和process_spider_output()来讲。
        """
        print(reponse1)
        return response

    def process_exception(self, request, exception, spider):
        """
        当下载处理器(download handler)或 process_request()(下载中间件)抛出异常(包括 IgnoreRequest 异常)时,Scrapy 调用 process_exception() 。
        :param request:  产生异常的request
        :param exception:  抛出的异常
        :param spider:  request对应的spider
        :return:
            当下载处理器(download  handler)或 process_request()(下载中间件)抛出异常(包括 IgnoreRequest 异常)时,Scrapy 调用 process_exception()。
            process_exception()应该返回以下之一: 返回 None、一个 Response 对象、或者一个 Request 对象。
            如果其返回 None,Scrapy 将会继续处理该异常,接着调用已安装的其他中间件的 process_exception()方法,直到所有中间件都被调用完毕,则调用默认的异常处理。
            如果其返回一个 Response 对象,则已安装的中间件链的 process_response()方法被调用。Scrapy 将不会调用任何其他中间件的 process_exception() 方法。
            如果其返回一个 Request 对象, 则返回的 request 将会被重新调用下载。这将停止中间件的 process_exception()方法执行,就如返回一个 response 的那样。
        """
        return None

    def spider_opened(self, spider):
        # 爬取的准备工作,创建engine的关键组件
        spider.logger.info(Spider opened: %s % spider.name)
技术分享图片
#1、与middlewares.py同级目录下新建proxy_handle.py
import requests

def get_proxy():
    return requests.get("http://127.0.0.1:5010/get/").text

def delete_proxy(proxy):
    requests.get("http://127.0.0.1:5010/delete/?proxy={}".format(proxy))
    
    

#2、middlewares.py
from Amazon.proxy_handle import get_proxy,delete_proxy

class DownMiddleware1(object):
    def process_request(self, request, spider):
        """
        请求需要被下载时,经过所有下载器中间件的process_request调用
        :param request:
        :param spider:
        :return:
            None,继续后续中间件去下载;
            Response对象,停止process_request的执行,开始执行process_response
            Request对象,停止中间件的执行,将Request重新调度器
            raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception
        """
        proxy="http://" + get_proxy()
        request.meta[download_timeout]=20
        request.meta["proxy"] = proxy
        print(为%s 添加代理%s  % (request.url, proxy),end=‘‘)
        print(元数据为,request.meta)

    def process_response(self, request, response, spider):
        """
        spider处理完成,返回时调用
        :param response:
        :param result:
        :param spider:
        :return:
            Response 对象:转交给其他中间件process_response
            Request 对象:停止中间件,request会被重新调度下载
            raise IgnoreRequest 异常:调用Request.errback
        """
        print(返回状态吗,response.status)
        return response


    def process_exception(self, request, exception, spider):
        """
        当下载处理器(download handler)或 process_request() (下载中间件)抛出异常
        :param response:
        :param exception:
        :param spider:
        :return:
            None:继续交给后续中间件处理异常;
            Response对象:停止后续process_exception方法
            Request对象:停止中间件,request将会被重新调用下载
        """
        print(代理%s,访问%s出现异常:%s %(request.meta[proxy],request.url,exception))
        import time
        time.sleep(5)
        delete_proxy(request.meta[proxy].split("//")[-1])
        request.meta[proxy]=http://+get_proxy()

        return request
配置代理

 

 官方文档:https://doc.scrapy.org/en/latest/topics/downloader-middleware.html

以上是关于Spider_Man_6 の Scrapy_Downloader Middleware(这是个需要针对一下的东西🐷🐷🐷)的主要内容,如果未能解决你的问题,请参考以下文章

Spider_Man_3 の selenium

Spider_Man_3 の BeautifulSoup

Spider_Man_5.2 の Mongodb_使用

初识Spider_Man(爬爬虫)

python 有效的Python项目23 classの特殊メソッド__call__のサンプル:リスト内の偶数を数える

WPFの获取任意元素的位置