使用代理IP中间件

有时我们需要编写自己的一些下载器中间件,如使用代理,更换user-agent等。

对于请求的中间件实现 process_request(requestspider);

对于处理回复中间件实现process_response(requestresponsespider);

以及异常处理实现 process_exception(requestexceptionspider)

  • process_request(request, spider)

每当scrapy进行一个request请求时,这个方法被调用。通常它可以返回1.None 2.Response对象 3.Request对象 4.抛出IgnoreRequest对象

如果返回None,Scrapy将继续处理此请求,执行所有其他中间件,直到最后调用适当的下载处理程序来执行请求(并下载其响应)。(通常返回None较常见 )

如果它返回一个Response对象,Scrapy就不会麻烦调用任何其他process_request()或process_exception()方法,或者相应的下载函数;它将返回该响应。安装的中间件的process_response()方法总是在每个响应上调用。

如果它返回一个request对象,Scrapy将停止调用process_Request方法并重新安排返回的请求。执行新返回的请求后,将对下载的响应调用相应的中间件链。

  • process_response(request, response, spider)

当请求发出去返回时这个方法会被调用,它会返回 1.Response对象 2.Request对象 3.抛出IgnoreRequest对象

1.若返回Response对象,它会被下个中间件中的process_response()处理

2.若返回Request对象,中间链停止,然后返回的Request会被重新调度下载

3.抛出IgnoreRequest,回调函数 Request.errback将会被调用处理,若没处理,将会忽略

  • process_exception(request, exception, spider)

当下载处理模块或process_request()抛出一个异常(包括IgnoreRequest异常)时,该方法被调用

通常返回None,它会一直处理异常

  • from_crawler(cls, crawler)

这个类方法通常是访问settings和signals的入口函数

自定义中间件

  1. 先开启settings中的设置

    DOWNLOADER_MIDDLEWARES = {
       'qianmu.middlewares.RandomProxyMiddleware': 749,
    }
    

    数字越小,越靠近引擎,数字越大越靠近下载器,所以数字越小的,process_request()优先处理;数字越大的,process_response()优先处理;若需要关闭某个中间件直接设为None即可

  2. 在middlewares.py创建中间件类

    from collections import defaultdict
    
    from scrapy import signals
    import random
    
    from scrapy.exceptions import NotConfigured
    
    class RandomProxyMiddleware(object):
    
        def __init__(self, settings):
            # 预先保留在settings中的代理IP列表
            self.proxies = settings.getlist('PROXIES')
            # Python中通过Key访问字典,当Key不存在时,会引发‘KeyError’异常。为了避免这种情况的发生,
            # 可以使用collections类中的defaultdict()方法来为字典提供默认值。默认类型int 初始值为0
            self.stats = defaultdict(int)
            self.max_failed = 3
    
        @classmethod
        def from_crawler(cls, crawler):
            if not crawler.settings.getbool('HTTPPROXY_ENABLED'):
                raise NotConfigured
            return cls(crawler.settings)
    
        def process_request(self, request, spider):
            if self.proxies and not request.meta.get('proxy') and request.url not in spider.start_urls:
                request.meta['proxy'] = random.choice(self.proxies)
    
        def process_response(self, request, response, spider):
            cur_proxy = request.meta.get('proxy')
            if response.status in (401, 403):
                self.stats[cur_proxy] += 1
                if self.stats[cur_proxy] >= self.max_failed:
                    self.remove_proxy(cur_proxy)
                    del request.meta['proxy']
                    return request
            return response
    
        def process_exception(self, request, exception, spider):
            cur_proxy = request.meta.get('proxy')
            if cur_proxy and isinstance(exception, (ConnectionRefusedError, TimeoutError)):
                self.remove_proxy(cur_proxy)
                del request.meta['proxy']
                # 若返回Request对象,中间链停止,然后返回的Request会被重新调度下载
                return request
            # 若返回Response对象,它会被下个中间件中的process_response()处理
            return response
    
        def remove_proxy(self, proxy):
            if proxy in self.proxies:
                self.proxies.remove(proxy)
    
    
posted @ 2020-03-29 22:02  Hhhighway  阅读(379)  评论(0编辑  收藏  举报