1. 程式人生 > >python之scrapy(四)downloader middlewares的用法

python之scrapy(四)downloader middlewares的用法

下載中介軟體是處於引擎(Engine)和下載器(DownLoader))之間的一層元件,可以有多個下載中介軟體被載入執行。

 

  • 當引擎傳遞請求給下載器的過程中,下載中介軟體可以對請求進行處理 (例如增加http header資訊,增加proxy資訊等);

  • 在下載器完成http請求,傳遞響應給引擎的過程中, 下載中介軟體可以對響應進行處理(例如進行gzip的解壓等)

 

1.使用說明

需要說明的是Scrapy其實已經提供了很多Downloader Middlewares,比如失敗重試、自動重定向等,他們被定義在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 settings --get=DOWNLOADER_MIDDLEWARES_BASE

來檢視該專案開啟的內建Downloader Middlewares的名稱和優先順序。

 

要啟用自定義的下載器中介軟體元件,將其加入到 DOWNLOADER_MIDDLEWARES 設定中。 該設定是一個字典(dict),鍵為中介軟體類的路徑,值為其中介軟體的順序(order)。

這裡是一個例子:

DOWNLOADER_MIDDLEWARES = {

'mySpider.middlewares.MyDownloaderMiddleware': 543,

}

2.核心方法

Scrapy內建的Downloader Middleware為Scrapy提供了基礎的功能,但在專案實戰裡面我們往往需要單獨自定義Downloader Middleware。

每個Downloader Middleware都定義了一個或多個類方法,核心方法主要有三個:

  • process_request(request, spider)

  • process_response(request, response, spider)

  • process_exception(request, exception, spider)

2.1 process_request(request, spider)

當每個request通過下載中介軟體時,該方法被呼叫。該方法主要有兩個引數:

  • request (Request 物件) – 被處理的request

  • spider (Spider 物件) – 該request對應的spider

 

process_request() 必須返回以下其中之一:一個 None 、一個 Response 物件、一個 Request 物件或 丟擲 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.2 process_response(request, response, spider)

DownLoader獲得Response之後,會先經過Downloader Middlewares。所以我們可以通過process_response()的方法,對Response進行處理。該方法有三個引數:

  • request (Request 物件) – response所對應的request

  • response (Response 物件) – 被處理的response

  • spider (Spider 物件) – response所對應的spider

 

process_response() 必須返回以下其中之一: 返回一個 Response 物件、 返回一個 Request 物件或raise一個 IgnoreRequest 異常。

  • 如果其返回一個 Response (可以與傳入的response相同,也可以是全新的物件), 該response會被在鏈中的其他中介軟體的 process_response() 方法處理。

  • 如果其返回一個 Request 物件,則中介軟體鏈停止, 返回的request會被重新排程下載。處理類似於 process_request() 返回request所做的那樣。

  • 如果其丟擲一個 IgnoreRequest 異常,則呼叫request的errback(Request.errback)。 如果沒有程式碼處理丟擲的異常,則該異常被忽略且不記錄(不同於其他異常那樣)。

2.3 process_exception(request, exception, spider)

當下載處理器(download handler)或 process_request() (下載中介軟體)丟擲異常(包括 IgnoreRequest 異常)時,Scrapy呼叫 process_exception()。主要有3個引數:

  • request (Request 物件) – response所對應的request。

  • exception (Exception 物件) – 丟擲的異常。

  • spider (Spider 物件) – response所對應的spider。

 

process_exception() 也是返回三者中的一個: 返回 None 、 一個 Response 物件、或者一個 Request 物件。

  • 如果其返回 None,Scrapy將會繼續處理該異常,接著呼叫已安裝的其他中介軟體的 process_exception() 方法,直到所有中介軟體都被呼叫完畢,則呼叫預設的異常處理。

  • 如果其返回一個 Response 物件,則已安裝的中介軟體鏈的 process_response() 方法被呼叫。Scrapy將不會呼叫任何其他中介軟體的 process_exception() 方法。

  • 如果其返回一個 Request 物件, 則返回的request將會被重新呼叫下載。這將停止中介軟體的 process_exception() 方法執行,就如返回一個response的那樣。 這個是非常有用的,就相當於如果我們失敗了可以在這裡進行一次失敗的重試,例如當我們訪問一個網站出現因為頻繁爬取被封ip就可以在這裡設定增加代理繼續訪問

3.實戰

3.1 process_request()和process_exception()方法

新建一個專案,命令如下所示:

scrapy startproject httptest

 

進入專案,新建一個spider,命令如下:

scrapy genspider http httpbin.org

 

可以修改spider內容如下:

import scrapy

 

class HttpSpider(scrapy.Spider):

name = 'http'

allowed_domains = ['httpbin.org']

start_urls = ['http://httpbin.org/']

 

def parse(self, response):

self.logger.debug(response.text)

執行此spider,執行如下命令:

scrapy crawl http

獲取request的response資訊:

{"args":{},

"headers":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",

"Accept-Encoding":"gzip,deflate",

"Accept-Language":"en","Connection":"close","Host":"httpbin.org",

"User-Agent":"Scrapy/1.5.0(+https://scrapy.org)"},

"origin":"116.77.73.255",

"url":"http://httpbin.org/get"}

 

針對middleware.py檔案中,新增process_request(request, spider)的方法:

import longging

class ProxyMiddleware(object):

logger = logging.getLogger(__name__)

def process_request(self, request, spider):

self.logger.debug('啟動代理!')

request.meta['proxy']='http://118.190.95.43:9001'

return None

 

 

在settings裡面,將DOWNLOADER_MIDDLEWARES註釋去掉,並設定如下內容:

DOWNLOADER_MIDDLEWARES = {
'httptest.middlewares.ProxyMiddleware': 543,
}

執行此spider,執行如下命令:

scrapy crawl http

得到內容如下

由此可見,代理已經生效,同時修改的狀態碼,已經生效。

 

3.2 process_exception()方法

進入專案,新建一個spider,命令如下:

scrapy genspider google www.google.com

 

輸出相應資訊:

 

會進行重試。

為了不影響演示,我們將關於重試的功能關閉掉:

DOWNLOADER_MIDDLEWARES = {

#'httptest.middlewares.ProxyMiddleware': 543,

'scrapy.downloadermiddlewares.retry.RetryMiddleware':None,

}

接下來,我們考慮用process_exception()來實現錯誤重試,那麼我們在spider裡面修改內容如下:

import scrapy

 

class GoogleSpider(scrapy.Spider):

name = 'google'

allowed_domains = ['www.google.com']

start_urls = ['http://www.google.com/']

 

def make_requests_from_url(self, url):

return scrapy.Request(url=url,dont_filter=True,meta={'download_timeout':10},callback=self.parse)

 

def parse(self, response):

self.logger.debug(response.text)

接下來實現,在middleware.py檔案裡面實現process_exception():

class ProxyMiddleware(object):

logger = logging.getLogger(__name__)

# def process_request(self, request, spider):

# self.logger.debug('啟動代理!')

# request.meta['proxy']='http://118.190.95.43:9001'

# return None

#

# def process_response(self, request, response, spider):

# response.status=201

# return response

def process_exception(self, request, exception, spider):

self.logger.debug('異常處理')

return request

在settings裡面,將DOWNLOADER_MIDDLEWARES設定如下內容:

DOWNLOADER_MIDDLEWARES = {

'httptest.middlewares.ProxyMiddleware': 543,

'scrapy.downloadermiddlewares.retry.RetryMiddleware':None,

}

得到的輸出如下:

說明進入了process_exception()方法裡面,會不斷進行錯誤重試。