一、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的詳細用法。
1. 使用說明
需要說明的是,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的使用方法。
2 . 核心方法
Scrapy內置的Downloader Middleware為Scrapy提供了基礎的功能,但在項目實戰中我們往往需要單獨定義Downloader Middleware。不用擔心,這個過程非常簡單,我們只需要實現某幾個方法即可。每個Downloader Middleware都定義了一個或多個方法的類,核心的方法有如下三個。
process_request(request, spider)
。process_response(request, response, spider)
。process_exception(request, exception, spider)
。
我們只需要實現至少一個方法,就可以定義一個Downloader Middleware。下面我們來看看這三個方法的詳細用法:
process_request(request, spider)
Request被Scrapy引擎調度給Downloader之前,process_request()方法就會被調用,也就是在Request從隊列里調度出來到Downloader下載執行之前,我們都可以用processrequest()方法對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執行。(如果返回None,Scrapy將繼續處理該request,執行其他中間件中的相應方法,直到合適的下載器處理函數被調用。)
- 當返回為Response對象時,更低優先級的Downloader Middleware的process_request()和process_exception()方法就不會被繼續調用,每個Downloader Middleware的process_response()方法轉而被依次調用。調用完畢之后,直接將Response對象發送給Spider來處理。(Scrapy將不會調用任何其他的process_request方法,將直接返回這個response對象。已經激活的中間件的process_response()方法將會在每個response返回時被調用。)
- 當返回為Request對象時,更低優先級的Downloader Middleware的process_request()方法會停止執行。這個Request會重新放到調度隊列里,其實它就是一個全新的Request,等待被調度。如果被Scheduler調度了,那么所有的Downloader Middleware的process_request()方法會被重新按照順序執行。(不再使用之前的request對象去下載數據,而是根據現在返回的request對象返回數據,接着執行其他下載器處理函數。)
- 如果IgnoreRequest異常拋出,則所有的Downloader Middleware的process_exception()方法會依次執行。如果沒有一個方法處理這個異常,那么Request的errorback()方法就會回調。如果該異常還沒有被處理,那么它便會被忽略。(如果這個方法中拋出了異常,則會調用process_exception方法。)
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()方法順次處理。
- 當返回為Respons巳對象時,更低優先級的Downloader Middleware的process_response()方法會繼續調用,繼續對該Response對象進行處理。
- 如果 gnoreRequest異常拋向,則Request的errorback()方法會回調。如果該異常還沒有被處理,那么它便會被忽略。
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()方法轉而被依次調用。(會將這個新的response對象傳給其他中間件,最終傳給爬蟲。)
- 當返回為Request對象時,更低優先級的Downloader Middleware的process_exception()也不再被繼續調用,該Request對象會重新放到調度隊列里面等待被調度,它相當於一個全新的Request。然后,該Request 又會被 process_request()方法順次處理。
(下載器鏈接切斷,返回的request會重新被下載器調度下載。) - 如果拋出一個異常,那么調用request的errback方法,如果沒有指定這個方法,那么會拋出一個異常。
以上內容便是這三個方法的詳細使用邏輯。在使用它們之前,請先對這三個方法的返回值的處理情況有一個清晰的認識。在自定義 Downloader Middleware的時候,也一定要注意每個方法的返回類型。
二、Spider Middleware 的用法
Spider Middleware是介入到Scrapy的Spider處理機制的鈎子框架。
當Downloader生成Response之后,Response會被發送給Spider,在發送給Spider之前,Response會首先經過Spider Middleware處理,當Spider處理生成Item和Request之后,Item和Request還會經過Spider Middleware的處理。
Spider Middleware有如下三個作用:
- 我們可以在Downloader生成的Response發送給Spider之前,也就是在Response發送給Spider之前對Response進行處理。
- 我們可以在Spider生成的Request發送給Scheduler之前,也就是在Request發送給Scheduler之前對Request進行處理。
- 我們可以在Spider生成的Item發送給Item Pipeline之前,也就是在Item發送給Item Pipeline之前對Item進行處理。
1. 使用說明
需要說明的是,Scrapy其實已經提供了許多Spider Middleware,它們被SPIDER_MIDDLEWARES_BASE這個變量所定義。
SPIDER_MIDDLEWARES_BASE變量的內容如下:
'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 50,
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': 500,
'scrapy.spidermiddlewares.referer.RefererMiddleware': 700,
'scrapy.spidermiddlewares.urllength.UrllengthMiddleware': 800,
'scrapy.spidermiddlewares.depth.DepthMiddleware': 900,
和Downloader Middleware一樣,Spider Middleware首先加入到SPIDER_MIDDLEWARES設置中,該設置會和Scrapy中SPIDER_MIDDLEWARES_BASE定義的Spider Middleware合並。然后根據鍵值的數字優先級排序,得到一個有序列表。第一個Middleware是最靠近引擎的,最后一個Middleware是最靠近Spider的。
2. 核心方法
Scrapy內置的Spider Middleware為Scrapy提供了基礎的功能。如果我們想要擴展其功能,只需要實現某幾個方法即可。
每個Spider Middleware都定義了以下一個或多個方法的類,核心方法有如下4個:
process_spider_input(response, spider)
。process_spider_output(response, result, spider)
。process_spider_exception(response, exception, spider)
。process_start_requests(start_requests, spider)
。
只需要實現其巾一個方法就可以定義一個Spider Middleware。下面我們來看看這4個方法的詳細用法:
process_spider_input(response, spider)
當Response被Spider Middleware處理時·,process_spider_input()方法被調用。
process_spider_input()
方法的參數有如下兩個:
- response,是Response對象,即被處理的Response。
- spider,是Spider對象,即該Response對應的Spider。
process_spider_input()應該返回None或者拋出一個異常。
- 如果它返回None,Scrapy將會繼續處理該Response,調用所有其他的Spider Middleware,直到Spider處理該Response。
- 如果它拋出一個異常,Scrapy將不會調用任何其他Spider Middleware的process_spider_input()方法,而調用Request的errback()方法。errback的輸出將會被重新輸入到中間件中,使用process_spider_output()方法來處理,當其拋出異常時則調用process_spider_exception()來處理。
process_spider_output(response, result, spider)
當Spider處理Response返回結果時,process_spider_output()方法被調用。
process_spider_output()
方法的參數有如下三個:
- response,是Response對象,即生成該輸出的Response。
- result,包含Request或Item對象的可迭代對象,即Spider返回的結果。
- spider,是Spider對象,即其結果對應的Spider。
process_spider_output()必須返回包含Request或Item對象的可迭代對象。
process_spider_exception(response, exception, spider)
當Spider或Spider Middleware的process_spider_input()方法拋出異常時,process_spider_exception()方法被調用。
process_spider_exception()
方法的參數有如下三個:
- response,是Response對象,即異常被拋出時被處理的Response。
- exception,是Exception對象,即被拋出的異常。
- spider,是Spider對象,即拋出該異常的Spider。
process_spider_exception()必須要么返回None,要么返回一個包含Response或Item對象的可迭代對象。
- 如果它返問None,Scrapy將繼續處理該異常,調用其他Spider Middleware中的process_spider_exception()方法,直到所有Spider Middleware都被調用。
- 如果它返回一個可迭代對象,則其他Spider Middleware的process_spider_output()方法被調用,其他的process_spider_exception()不會被調用。
process_start_requests(start_requests, spider)
process_start_requests()方法以Spider啟動的Request為參數被調用,執行的過程類似於process_spider_output(),只不過它沒有相關聯的Response,並且必須返回Request。
process_start_requests()
方法的參數有如下兩個:
- start_requests,是包含Request的可迭代對象,即Start Requests。
- spider,是Spider對象,即Start Requests所屬的Spider。
process_start_requests()必須返回另一個包含Request對象的可迭代對象。
3 . 結語
本節介紹了Spider Middleware的基本原理和自定義Spider Middleware的方法。Spider Middleware使用的頻率不如Downloader Middleware的高,在必要的情況下它可以用來方便數據的處理。