Scrapy框架的使用之Downloader Middleware的用法

Downloader Middleware即下载中间件,它是处于Scrapy的Request和Response之间的处理模块。我们首先来看看它的架构,如下图所示。

Scheduler从队列中拿出一个Request发送给Downloader执行下载,这个过程会经过Downloader Middleware的处理。另外,当Downloader将Request下载完成得到Response返回给Spider时会再次经过Downloader Middleware处理。

也就是说,Downloader Middleware在整个架构中起作用的位置是以下两个:

  • 在Scheduler调度出队列的Request发送给Doanloader下载之前,也就是我们可以在Request执行下载之前对其进行修改。
  • 在下载后生成的Response发送给Spider之前,也就是我们可以在生成Resposne被Spider解析之前对其进行修改。

Downloader Middleware的功能十分强大,修改User-Agent、处理重定向、设置代理、失败重试、设置Cookies等功能都需要借助它来实现。下面我们来了解一下Downloader Middleware的详细用法。

一、使用说明

需要说明的是,Scrapy其实已经提供了许多Downloader Middleware,比如负责失败重试、自动重定向等功能的Middleware,它们被DOWNLOADER_MIDDLEWARES_BASE变量所定义。

DOWNLOADER_MIDDLEWARES_BASE变量的内容如下所示:

{
    '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,
}

复制

这是一个字典格式,字典的键名是Scrapy内置的Downloader Middleware的名称,键值代表了调用的优先级,优先级是一个数字,数字越小代表越靠近Scrapy引擎,数字越大代表越靠近Downloader,数字小的Downloader Middleware会被优先调用。

如果自己定义的Downloader Middleware要添加到项目里,DOWNLOADER_MIDDLEWARES_BASE变量不能直接修改。Scrapy提供了另外一个设置变量DOWNLOADER_MIDDLEWARES,我们直接修改这个变量就可以添加自己定义的Downloader Middleware,以及禁用DOWNLOADER_MIDDLEWARES_BASE里面定义的Downloader Middleware。下面我们具体来看看Downloader Middleware的使用方法。

二、核心方法

Scrapy内置的Downloader Middleware为Scrapy提供了基础的功能,但在项目实战中我们往往需要单独定义Downloader Middleware。不用担心,这个过程非常简单,我们只需要实现某几个方法即可。

每个Downloader Middleware都定义了一个或多个方法的类,核心的方法有如下三个。

  • process_request(request, spider)
  • process_response(request, response, spider)
  • process_exception(request, exception, spider)

我们只需要实现至少一个方法,就可以定义一个Downloader Middleware。下面我们来看看这三个方法的详细用法。

1. process_request(request, spider)

Request被Scrapy引擎调度给Downloader之前,process_request()方法就会被调用,也就是在Request从队列里调度出来到Downloader下载执行之前,我们都可以用process_request()方法对Request进行处理。方法的返回值必须为None、Response对象、Request对象之一,或者抛出IgnoreRequest异常。

process_request()方法的参数有如下两个。

  • request,是Request对象,即被处理的Request。
  • spider,是Spdier对象,即此Request对应的Spider。

返回类型不同,产生的效果也不同。下面归纳一下不同的返回情况。

  • 当返回是None时,Scrapy将继续处理该Request,接着执行其他Downloader Middleware的process_request()方法,一直到Downloader把Request执行后得到Response才结束。这个过程其实就是修改Request的过程,不同的Downloader Middleware按照设置的优先级顺序依次对Request进行修改,最后送至Downloader执行。
  • 当返回为Response对象时,更低优先级的Downloader Middleware的process_request()process_exception()方法就不会被继续调用,每个Downloader Middleware的process_response()方法转而被依次调用。调用完毕之后,直接将Response对象发送给Spider来处理。
  • 当返回为Request对象时,更低优先级的Downloader Middleware的process_request()方法会停止执行。这个Request会重新放到调度队列里,其实它就是一个全新的Request,等待被调度。如果被Scheduler调度了,那么所有的Downloader Middleware的process_request()方法会被重新按照顺序执行。
  • 如果IgnoreRequest异常抛出,则所有的Downloader Middleware的process_exception()方法会依次执行。如果没有一个方法处理这个异常,那么Request的errorback()方法就会回调。如果该异常还没有被处理,那么它便会被忽略。

2. process_response(request, response, spider)

Downloader执行Request下载之后,会得到对应的Response。Scrapy引擎便会将Response发送给Spider进行解析。在发送之前,我们都可以用process_response()方法来对Response进行处理。方法的返回值必须为Request对象、Response对象之一,或者抛出IgnoreRequest异常。

process_response()方法的参数有如下三个。

  • request,是Request对象,即此Response对应的Request。
  • response,是Response对象,即此被处理的Response。
  • spider,是Spider对象,即此Response对应的Spider。

下面归纳一下不同的返回情况。

  • 当返回为Request对象时,更低优先级的Downloader Middleware的process_response()方法不会继续调用。该Request对象会重新放到调度队列里等待被调度,它相当于一个全新的Request。然后,该Request会被process_request()方法顺次处理。
  • 当返回为Response对象时,更低优先级的Downloader Middleware的process_response()方法会继续调用,继续对该Response对象进行处理。
  • 如果IgnoreRequest异常抛出,则Request的errorback()方法会回调。如果该异常还没有被处理,那么它便会被忽略。

3. process_exception(request, exception, spider)

当Downloader或process_request()方法抛出异常时,例如抛出IgnoreRequest异常,process_exception()方法就会被调用。方法的返回值必须为None、Response对象、Request对象之一。

process_exception()方法的参数有如下三个。

  • request,是Request对象,即产生异常的Request。
  • exception,是Exception对象,即抛出的异常。
  • spdier,是Spider对象,即Request对应的Spider。

下面归纳一下不同的返回值。

  • 当返回为None时,更低优先级的Downloader Middleware的process_exception()会被继续顺次调用,直到所有的方法都被调度完毕。
  • 当返回为Response对象时,更低优先级的Downloader Middleware的process_exception()方法不再被继续调用,每个Downloader Middleware的process_response()方法转而被依次调用。
  • 当返回为Request对象时,更低优先级的Downloader Middleware的process_exception()也不再被继续调用,该Request对象会重新放到调度队列里面等待被调度,它相当于一个全新的Request。然后,该Request又会被process_request()方法顺次处理。

以上内容便是这三个方法的详细使用逻辑。在使用它们之前,请先对这三个方法的返回值的处理情况有一个清晰的认识。在自定义Downloader Middleware的时候,也一定要注意每个方法的返回类型。

下面我们用一个案例实战来加深一下对Downloader Middleware用法的理解。

三、项目实战

新建一个项目,命令如下所示:

scrapy startproject scrapydownloadertest

复制

新建了一个Scrapy项目,名为scrapydownloadertest。进入项目,新建一个Spider,命令如下所示:

scrapy genspider httpbin httpbin.org

复制

新建了一个Spider,名为httpbin,源代码如下所示:

import scrapy
class HttpbinSpider(scrapy.Spider):
    name = 'httpbin'
    allowed_domains = ['httpbin.org']
    start_urls = ['http://httpbin.org/']

    def parse(self, response):
        pass

复制

接下来我们修改start_urls为:[http://httpbin.org/](http://httpbin.org/)。随后将parse()方法添加一行日志输出,将response变量的text属性输出出来,这样我们便可以看到Scrapy发送的Request信息了。

修改Spider内容如下所示:

import scrapy

class HttpbinSpider(scrapy.Spider):
    name = 'httpbin'
    allowed_domains = ['httpbin.org']
    start_urls = ['http://httpbin.org/get']

    def parse(self, response):
        self.logger.debug(response.text)

复制

接下来运行此Spider,执行如下命令:

scrapy crawl httpbin

复制

Scrapy运行结果包含Scrapy发送的Request信息,内容如下所示:

{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 
    "Accept-Encoding": "gzip,deflate,br", 
    "Accept-Language": "en", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "Scrapy/1.4.0 (+http://scrapy.org)"
  }, 
  "origin": "60.207.237.85", 
  "url": "http://httpbin.org/get"
}

复制

我们观察一下Headers,Scrapy发送的Request使用的User-Agent是Scrapy/1.4.0(+http://scrapy.org),这其实是由Scrapy内置的`UserAgentMiddleware`设置的,`UserAgentMiddleware`的源码如下所示:

from scrapy import signals

class UserAgentMiddleware(object):
    def __init__(self, user_agent='Scrapy'):
        self.user_agent = user_agent

    @classmethod
    def from_crawler(cls, crawler):
        o = cls(crawler.settings['USER_AGENT'])
        crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
        return o

    def spider_opened(self, spider):
        self.user_agent = getattr(spider, 'user_agent', self.user_agent)

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

复制

from_crawler()方法中,首先尝试获取settings里面USER_AGENT,然后把USER_AGENT传递给__init__()方法进行初始化,其参数就是user_agent。如果没有传递USER_AGENT参数就默认设置为Scrapy字符串。我们新建的项目没有设置USER_AGENT,所以这里的user_agent变量就是Scrapy。接下来,在process_request()方法中,将user-agent变量设置为headers变量的一个属性,这样就成功设置了User-Agent。因此,User-Agent就是通过此Downloader Middleware的process_request()方法设置的。

修改请求时的User-Agent可以有两种方式:一是修改settings里面的USER_AGENT变量;二是通过Downloader Middleware的process_request()方法来修改。

第一种方法非常简单,我们只需要在setting.py里面加一行USER_AGENT的定义即可:

USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'

复制

一般推荐使用此方法来设置。但是如果想设置得更灵活,比如设置随机的User-Agent,那就需要借助Downloader Middleware了。所以接下来我们用Downloader Middleware实现一个随机User-Agent的设置。

在middlewares.py里面添加一个RandomUserAgentMiddleware的类,如下所示:

import random

class RandomUserAgentMiddleware():
    def __init__(self):
        self.user_agents = [
            'Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)',
            'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2',
            'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1'
        ]

    def process_request(self, request, spider):
        request.headers['User-Agent'] = random.choice(self.user_agents)

复制

我们首先在类的__init__()方法中定义了三个不同的User-Agent,并用一个列表来表示。接下来实现了process_request()方法,它有一个参数request,我们直接修改request的属性即可。在这里我们直接设置了request变量的headers属性的User-Agent,设置内容是随机选择的User-Agent,这样一个Downloader Middleware就写好了。

不过,要使之生效我们还需要再去调用这个Downloader Middleware。在settings.py中,将DOWNLOADER_MIDDLEWARES取消注释,并设置成如下内容:

DOWNLOADER_MIDDLEWARES = {
   'scrapydownloadertest.middlewares.RandomUserAgentMiddleware': 543,
}

复制

接下来我们重新运行Spider,就可以看到User-Agent被成功修改为列表中所定义的随机的一个User-Agent了:

{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 
    "Accept-Encoding": "gzip,deflate,br", 
    "Accept-Language": "en", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)"
  }, 
  "origin": "60.207.237.85", 
  "url": "http://httpbin.org/get"
}

复制

我们就通过实现Downloader Middleware并利用process_request()方法成功设置了随机的User-Agent。

另外,Downloader Middleware还有process_response()方法。Downloader对Request执行下载之后会得到Response,随后Scrapy引擎会将Response发送回Spider进行处理。但是在Response被发送给Spider之前,我们同样可以使用process_response()方法对Response进行处理。比如这里修改一下Response的状态码,在RandomUserAgentMiddleware添加如下代码:

def process_response(self, request, response, spider):
    response.status = 201
    return response

复制

我们将response变量的status属性修改为201,随后将response返回,这个被修改后的Response就会被发送到Spider。

我们再在Spider里面输出修改后的状态码,在parse()方法中添加如下的输出语句:

self.logger.debug('Status Code: ' + str(response.status))

复制

重新运行之后,控制台输出了如下内容:

[httpbin] DEBUG: Status Code: 201

复制

可以发现,Response的状态码成功修改了。

因此要想对Response进行后处理,就可以借助于process_response()方法。

另外还有一个process_exception()方法,它是用来处理异常的方法。如果需要异常处理的话,我们可以调用此方法。不过这个方法的使用频率相对低一些,在此不用实例演示。

四、本节代码

本节源代码为:https://github.com/Python3WebSpider/ScrapyDownloaderTest。

五、结语

本节讲解了Downloader Middleware的基本用法。此组件非常重要,是做异常处理和反爬处理的核心。后面我们会在实战中应用此组件来处理代理、Cookies等内容。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Scrapy中间件是一种可以在Scrapy引擎和Spider之间插入自定义逻辑的机制。它可以用来完成如下任务: - 处理请求和响应 - 添加额外的请求 - 修改请求和响应 要使用中间件,需要在项目的settings.py文件中进行配置。可以在该文件中配置中间件的先后顺序。 - 为爬虫添加中间件,可以在settings.py文件中的SPIDER_MIDDLEWARES设置中添加中间件类。 - 为下载器添加中间件,可以在settings.py文件中的DOWNLOADER_MIDDLEWARES设置中添加中间件类。 通常情况下可以选择继承scrapy自带的中间件或自己编写中间件,调用自己需要的方法 例如定义一个中间件类 ``` class MyMiddleware: def process_request(self, request, spider): # request 处理逻辑 pass def process_response(self, request, response, spider): #response 处理逻辑 pass ``` 在settings.py中配置中间件 ``` DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.MyMiddleware': 543, } ``` 然后你就可以在爬虫中使用这个中间件了。 ### 回答2: Scrapy是一个用于爬取网站数据的Python框架,它提供了许多强大的功能,其中一个就是中间件(Middleware)。 中间件是Scrapy处理请求和响应的机制,可以在请求发送给下载器之前和响应返回给爬虫之前对其进行处理。使用中间件可以在爬虫运行过程中插入自定义的功能,例如修改请求参数、处理异常、添加代理等。 使用Scrapy中的中间件非常简单,只需要按照以下步骤进行操作: 1. 创建一个中间件类,该类需要继承ScrapyMiddleware类,并实现其中的方法,如下所示: ``` from scrapy import signals class MyMiddleware(object): def __init__(self): pass @classmethod def from_crawler(cls, crawler): middleware = cls() crawler.signals.connect(middleware.spider_opened, signal=signals.spider_opened) crawler.signals.connect(middleware.spider_closed, signal=signals.spider_closed) return middleware def process_request(self, request, spider): # 对请求进行处理 return None def process_response(self, request, response, spider): # 对响应进行处理 return response def spider_opened(self, spider): pass def spider_closed(self, spider): pass ``` 2. 在Scrapy的配置文件settings.py中添加中间件,如下所示: ``` DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.MyMiddleware': 543, 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 543, 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 544, } ``` 注意:中间件的优先级通过数字进行设置,数字越小,优先级越高。 3. 根据需求实现中间件的具体功能。例如,可以在`process_request`方法中实现修改请求参数的功能,在`process_response`方法中实现处理异常的逻辑。 以上就是使用Scrapy中间件的简单介绍和示例,通过使用中间件,我们可以在爬虫运行过程中对请求和响应进行灵活的处理,扩展了Scrapy框架的功能和灵活性。 ### 回答3: Scrapy是一个用于爬取网站数据的Python框架,它提供了一个灵活且可扩展的机制来处理不同网站的爬取需求。其中,middleware(中间件)是Scrapy中一个非常重要的组件,用于在请求和响应之间进行预处理和后处理操作。 Scrapy中的middleware可以用于修改请求和响应的内容,例如添加、修改或删除请求头、对请求进行代理、修改响应的内容或状态码等。通过使用middleware,可以在爬取过程中实现多种自定义的功能,例如用户代理轮换、请求去重、反爬虫机制等。 使用middleware的步骤如下: 1. 创建一个自定义的middleware类并继承ScrapyMiddleware类。 2. 在Middleware类中实现预处理和后处理操作的具体逻辑,可以通过重写相应的方法来实现,例如process_request、process_response等。 3. 在Scrapy的配置文件settings.py中启用该middleware,即将其添加到DOWNLOADER_MIDDLEWARES配置项中,并设置其优先级(数值越小,优先级越高)。 在使用middleware时,可以根据实际需求选择使用Scrapy已经内置的middleware,或者自定义一个middleware类来满足特定的爬取需求。Scrapy内置的一些middleware包括UserAgentMiddleware(设置请求的user agent)、RetryMiddleware(处理请求失败或异常情况)等。 总之,middlewareScrapy中是一个非常重要的组件,通过使用middleware可以实现对请求和响应的灵活处理,从而提高爬虫的效率和功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值