zoukankan      html  css  js  c++  java
  • 7.scrapy中间件

    scrapy中间件

    1. 中间件

    1.1 什么是中间件

    如果有学过django的同学,应该对这个名词不陌生了,在django中,中间件可以对请求做统一批量的处理

    那么在爬虫中,中间件的作用也是做批量处理的,比如把所有请求的请求头添加一个值等等等。

    由于爬虫是一个发请求,获取响应的过程,所以在scrapy框架中有两个中间件。

    2. 两种中间件

    在scrapy框架中所谓的中间件本质上就是一个类,里面有一些方法。

    当然你想应用中间件,必须要在settings文件中注册一下,优先级仍然是数值小的优先级高,一般放在上面。

    # 爬虫中间件
    SPIDER_MIDDLEWARES = {
       'spider1.middlewares.Spider1SpiderMiddleware': 543,
    }
    
    # 下载中间件
    DOWNLOADER_MIDDLEWARES = {
       'spider1.middlewares.Spider1DownloaderMiddleware': 543,
    }
    
    

    2.1 爬虫中间件

    实际上中间件的方法只有四个就行了,但是框架给我们绑定了一个爬虫打开时的信号。

    from scrapy import signals
    
    
    class Spider1SpiderMiddleware:
    
    
        @classmethod
        def from_crawler(cls, crawler):
            # This method is used by Scrapy to create your spiders.
            s = cls()
            # 创建spider(爬虫对象)的时候,注册一个信号
            # 信号: 当爬虫的打开的时候 执行 spider_opened 这个方法
            crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
            return s
    
        def process_spider_input(self, response, spider):
            # 下载完成后,执行,然后交给parse处理
            return None
    
        def process_spider_output(self, response, result, spider):
            """
            经历过parse函数之后执行
            :param response: 上一次请求返回的结果
            :param result: yield的对象 包含 [item/Request] 对象的可迭代对象
            :param spider: 当前爬虫对象
            :return: 返回Request对象 或 Item对象
            """
            for i in result:
                yield i
    
        def process_spider_exception(self, response, exception, spider):
        
            """如果执行parse抛出异常的话 会执行这个函数 默认不对异常处理交给下一个中间件处理"""
            pass
    
        def process_start_requests(self, start_requests, spider):
         
            """
            爬虫启动时调用
            :param start_requests: 包含 Request 对象的可迭代对象
            :param spider:
            :return: Request 对象
            """
            for r in start_requests:
                yield r
    
        def spider_opened(self, spider):
            # 生成爬虫日志
            spider.logger.info('Spider opened: %s' % spider.name)
    

    2.1.1 方法解析

    爬虫开始时有个起始url,这个时候会经过爬虫中间件的 process_start_requests 方法,然后去下载,接着

    我们看到 process_spider_input 和 process_spider_output 这两个方法中参数都有一个response参数可以判断出

    这两个方法都是在下载完成后,拿到response之后才会执行的。

    当下载完成后再经过爬虫中间件,这个时候会执行 process_spider_input 方法,这个时候的response中有

    包含这次请求的所有内容,其中请求的request对象也被封装成了 response.resquest 。

    然后来到 spider 文件中,经历parse方法,这个时候返回值可能是Request对象或者item对象,然后如果

    parse 方法中抛出异常会执行爬虫中间件的 process_spider_exception 方法,一般情况下无异常,继续走爬虫

    中间件中的 process_spider_output 方法。这就是爬虫中间件中方法的在什么地方执行的过程。

    至于没讲到的那两个方法,是基于信号做出的关于爬虫开启时日志的拓展。对爬虫中间件无影响,后面我会将

    到关于信号实现的自定义拓展。

    2.2 下载中间件

    默认下载中间件源码:

    class Spider1DownloaderMiddleware:
    
    
        @classmethod
        def from_crawler(cls, crawler):
            # This method is used by Scrapy to create your spiders.
            # 这个方法同上,和爬虫中间件一样的功能
            s = cls()
            crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
            return s
    
        def process_request(self, request, spider):
      
            """
            请求需要被下载时,经过所有下载中间件的process_request调用
            spider处理完成,返回时调用
            :param request:
            :param spider:
            :return:
                None,继续往下执行,去下载
                Response对象,停止process_request的执行,开始执行process_response
                Request对象,停止中间件的执行,将Request重新放到调度器中
                raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception
            """
            return None
    
        def process_response(self, request, response, spider):
    
            """
            下载得到响应后,执行
            :param request: 请求对象
            :param response: 响应对象
            :param spider: 爬虫对象
            :return:
                返回request对象,停止中间件,将Request对象重新放到调度器中
                返回response对象,转交给其他中间件process_response
                raise IgnoreRequest 异常: 调用Request.errback
            """
            return response
    
        def process_exception(self, request, exception, spider):
            
            """当下载处理器(download handler)或process_request() (下载中间件)抛出异常
                :return
                    None: 继续交给后续中间件处理异常
                    Response对象: 停止后续process_exception方法
                    Request对象: 停止中间件,request将会被重新调用下载
            """
            pass
    
        def spider_opened(self, spider):
            spider.logger.info('Spider opened: %s' % spider.name)
    

    2.2.1 process_request

    process_request(self, request, spider)

    该方法是下载器中间件类中的一个方法,该方法是每个请求从引擎发送给下载器下载之前都会经过该方法。所以该

    方法经常用来处理请求头的替换,IP的更改,Cookie等的替换。

    参数:

    • request (Request 对象) – 处理的request

    • spider (Spider 对象) – 该request对应的spider

    返回值:

    返回 None:Scrapy将继续处理该request,执行其他的中间件的相应方法,直到合适的下载器处理函数

    (download handler)被调用,该request被执行(其response被下载)。

    如果其返回Response对象:Scrapy将不会调用任何其他的 process_request ( ) 或 process_exception ( )

    方法,或相应地下载函数; 其将返回response。

    如果其返回Request对象,Scrapy则停止调用 process_request 方法并重新调度返回的request。当新返回的

    request被执行后, 相应地中间件链将会根据下载的response被调用。

    如果其raise一个 IgnoreRequest 异常,则安装的下载中间件的 process_exception ( ) 方法会被调用。如

    果没有任何一个方法处理该异常, 则request的 errback(Request.errback) 方法会被调用。如果没有代码处

    理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)。

    2.2.2 process_response

    process_response(self, request, response, spider)

    该方法是下载器中间件类中的一个方法,该方法是每个响应从下载器发送给spider之前都会经过该方法。

    参数:

    • request (Request 对象) – response所对应的request

    • response (Response 对象) – 被处理的response

    • spider (Spider 对象) – response所对应的spider

    返回值:

    如果其返回一个Response (可以与传入的response相同,也可以是全新的对象), 该response会被其他中间件的

    process_response() 方法处理。

    如果其返回一个Request对象,则返回的request会被重新调度下载。处理类似于 process_request() 返回

    request所做的那样。

    如果其抛出一个 IgnoreRequest 异常,则调用request的 errback(Request.errback) 。如果没有代码处理

    抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。

    2.2.3 随机替换User-Agent

    一般门户网站都会在请求头中的User-Agent中设置反爬,所以我们经常会在写爬虫的时候,要手动处理

    User-Agent,但是一般情况下我们都是大批量爬取,如果在这里指定一个 User-Agent 的话,也会可能被

    网站检测出来是爬虫,这里我们提供一种随机 User-Agent 的方法。

    import Faker
    
    class UserAgentDownloaderMiddleware(object):
        
        def __init__(self):
            self.fake = faker.Faker()
            
        def process_request(self, request, spider):
            # 随机生成一个User-Agent
            useragent = self.fake.user_agent()
            # 更新请求头的内容
            request.headers.update({"User-Agent": useragent})
            return None
    

    2.2.4 设置cookie值

    有时候我们想要爬取的数据可以需要登录才能看到,这时候我们就需要登录网页。登陆后的网页一般都会在本地保存
    该网页的登录信息Cookies在本地。只要获取该Cookie,那么在以后跳转到其他网页的时候,只需要携带该Cookie即可。

    class UserAgentDownloaderMiddleware(object):
        
        def __init__(self):
            self.fake = faker.Faker()
            
        def process_request(self, request, spider):
            # 随机生成一个User-Agent
            useragent = self.fake.user_agent()
            # 更新请求头的内容
            request.headers.update({"useragent": useragent})
            return None
    

    2.2.5 设置代理

    工作中你用爬虫,一定最常用的就是代理,这里我们可以自定义代理中间件

    def get_proxy():
        """获取代理的函数"""
        response = requests.get('http://134.175.188.27:5010/get/')
        data = response.json()
        return data["proxy"]
    
    
    class ProxyDownloaderMiddleware(object):
        """代理中间件"""
        def process_request(self, request, spider):
            request.meta['proxy'] = get_proxy()
            return None
    

    设置了代理之后,可能会出现代理不可用,一般我们都是在配置文件中设置重试。

    RETRY_ENABLED = True  # 是否开启超时重试
    RETRY_TIMES = 2       # initial response + 2 retries = 3 requests 重试次数
    RETRY_HTTP_CODES =  [500, 502, 503, 504, 522, 524, 408, 429] # 重试的状态码
    DOWNLOAD_TIMEOUT = 1  # 1秒没有请求到数据,主动放弃
    
  • 相关阅读:
    ListView之setEmptyView的问题
    AdapterView的相关知识。
    分享一个程序猿的真实的爱情故事
    C#中的explicit和implicit了解一下吧
    通俗易懂,什么是.NET Core以及.NET Core能做什么
    目前下载VS2017你可能会遇到这个坑
    .NET Core中使用Dapper操作Oracle存储过程最佳实践
    分享一个.NET平台开源免费跨平台的大数据分析框架.NET for Apache Spark
    从ASP.NET Core2.2到3.0你可能会遇到这些问题
    What?VS2019创建新项目居然没有.NET Core3.0的模板?Bug?
  • 原文地址:https://www.cnblogs.com/xcymn/p/13258162.html
Copyright © 2011-2022 走看看