Python笔记:爬虫框架Scrapy之Downloader Middleware的使用

Downloader Middleware的功能

在Downloader Middleware的功能十分强大:

  • 可以修改User-Agent
  • 处理重定向
  • 设置代理
  • 失败重试
  • 设置Cookies等

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

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

Scrapy中的内建Downloader Middleware

  • 在Scrapy中已经提供了许多Downloader Middleware,如:负责失败重试、自动重定向等中间件:
  • 它们都被定义到DOWNLOADER_MIDDLEWARES_BASE变量中。
  • 注:下面的配置,是全局配置,不要修改,如果要修改,去修改项目中的配置!
# 在python3.6/site-packages/scrapy/settings/default_settings.py默认配置中

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 Middleware中间件

我们可以通过项目的DOWNLOADER_MIDDLEWARES变量设置来添加自己定义的Downloader Middleware。其中Downloader Middleware有三个核心方法:

1 ) process_request(request,spider)

  • 当每个request通过下载中间件时,该方法被调用,这里有一个要求,该方法必须返回以下三种中的任意一种:None、返回一个Response对象、返回一个Request对象raise IgnoreRequest。三种返回值的作用是不同的。

  • None: Scrapy将继续处理该request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download handler)被调用,该request被执行(其response被下载)。

  • Response对象:Scrapy将不会调用任何其他的process_request()或process_exception() 方法,或相应地下载函数;其将返回该response。 已安装的中间件的 process_response() 方法则会在每个response返回时被调用。

  • Request对象:Scrapy则停止调用 process_request方法并重新调度返回的request。当新返回的request被执行后, 相应地中间件链将会根据下载的response被调用。

  • raise一个IgnoreRequest异常:则安装的下载中间件的 process_exception() 方法会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback)方法会被调用。如果没有代码处理抛出的异常,则该异常被忽略且不记录。

2 ) process_response(request,response,spider)

  • process_response的返回值也是有三种:response对象request对象,或者raise一个IgnoreRequest异常

  • 如果其返回一个Response(可以与传入的response相同,也可以是全新的对象),该response会被在链中的其他中间件的 process_response() 方法处理。

  • 如果其返回一个 Request 对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于 process_request() 返回request所做的那样。

  • 如果其抛出一个 IgnoreRequest 异常,则调用request的errback(Request.errback)

  • 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。

  • 这里我们写一个简单的例子还是上面的项目,我们在中间件中继续添加如下代码:

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

3 ) process_exception(request,exception,spider)

  • 当下载处理器(download handler)或 process_request() (下载中间件)抛出异常(包括 IgnoreRequest 异常)时,Scrapy调用 process_exception()

  • process_exception() 也是返回三者中的一个: 返回 None 、 一个 Response 对象、或者一个 Request 对象

  • 如果其返回 None ,Scrapy将会继续处理该异常,接着调用已安装的其他中间件的 process_exception() 方法,直到所有中间件都被调用完毕,则调用默认的异常处理。

  • 如果其返回一个 Response 对象,则已安装的中间件链的 process_response() 方法被调用。Scrapy将不会调用任何其他中间件的 process_exception() 方法。

项目实践

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

$ scrapy startproject douban

2 ) 新建一个Spider类, 名字为dbbook,命令如下所示:

$ cd douban
$ scrapy genspider dbbook book.douban.com

编写爬虫代码:

# -*- coding: utf-8 -*-
import scrapy

class DbbookSpider(scrapy.Spider):
    name = 'dbbook'
    allowed_domains = ['book.douban.com']
    start_urls = ['https://book.douban.com/top250?start=0']

    def parse(self, response):
        #print("状态:")
        pass

3 ) 执行爬虫命令,排除错误

$ scrapy crawl dbbook #结果返回403错误(服务器端拒绝访问)。

原因分析:默认scrapy框架请求信息中的User-Agent的值为:Scrapy/1.5.0(http://scrapy.org).

解决方案:我们可以在settings.py配置文件中:设置 USER_AGENT 或者DEFAULT_REQUEST_HEADERS信息:

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

# 或
# ...
DEFAULT_REQUEST_HEADERS = {
   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
   'Accept-Language': 'en',
   'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36',
}
# ...

还有一种解决方案:

middlewares.py文件中找到DoubanDownloaderMiddleware类, 在里面的process_request方法中处理如下:

def process_request(self, request, spider):
    #输出header头信息
    print(request.headers)
    #伪装浏览器用户
    request.headers['user-agent']='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36'
    return None

4 ) 开启Downloader Middleware中间件

在项目的settings.py配置文件中:开启设置DOWNLOADER_MIDDLEWARES信息:

DOWNLOADER_MIDDLEWARES = {
    'douban.middlewares.DoubanDownloaderMiddleware': 543,
}

点击这里查看代码地址

发布了369 篇原创文章 · 获赞 169 · 访问量 66万+

猜你喜欢

转载自blog.csdn.net/Tyro_java/article/details/103933163