本文地址:http://www.reibang.com/p/2f80c0fb818e
眾所周知Scrapy
有一個非常強大的優(yōu)點累提,就是其結(jié)構(gòu)非常模塊化,想要自定義的擴展功能非常方便署恍。而其模塊化的思想很大一部分體現(xiàn)在其Middleware
組件上。
Middleware
其實是一個輕量級、較底層的鉤子框架,用來全局的去處理相對應(yīng)的內(nèi)容扣囊。得益于其模塊化的結(jié)構(gòu),編寫和添加一個Middleware
是非常輕松方便的绒疗。
Scrapy
中主要有兩種Middleware
侵歇,一種是Downloader Middleware
,一種是Spider Middleware
吓蘑。
下載中間件(Downloader Middleware)
如簡介中的流程圖所示惕虑,Downloader Middleware
處于Downloader
和引擎之間,所以它可以處理進出Downloader
的Request和Response磨镶。
啟用下載中間件(Downloader Middleware)
首先溃蔫,我們要啟用一個Downloader Middleware
,需要在settings
中進行相應(yīng)的設(shè)置琳猫,如下是一個簡單例子:
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.CustomDownloaderMiddleware': 543,
}
這個字典的鍵名是Downloader Middleware
類的路徑伟叛,值是其優(yōu)先級。
DOWNLOADER_MIDDLEWARES
設(shè)置將會與默認的DOWNLOADER_MIDDLEWARES_BASE
混合脐嫂,然后按照對應(yīng)的優(yōu)先級進行排序统刮。優(yōu)先級數(shù)值越小,越靠近引擎這邊账千,數(shù)值越大侥蒙,越靠近Downloader
一端。
換句話說匀奏,Downloader Middleware
中的process_request()
方法鞭衩,將按照優(yōu)先級逐漸增大的順序依次調(diào)用,而process_response()
方法調(diào)用順序正好相反娃善。
DOWNLOADER_MIDDLEWARES_BASE
是Scrapy
中默認已經(jīng)啟用的一些默認的下載中間件:
{
'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 Middleware
插入到指定的位置论衍,那么你需要將優(yōu)先級設(shè)置為相對應(yīng)的中間件優(yōu)先級之間。
如果想要關(guān)閉DOWNLOADER_MIDDLEWARES_BASE
中某些中間件聚磺,那么你可以在DOWNLOADER_MIDDLEWARES
中將其值設(shè)置為None坯台。如下所示:
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.CustomDownloaderMiddleware': 543,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
}
自定義下載中間件(Downloader Middleware)
每個Middleware
都是一個定義了以下一個或多個方法的Python類。
class scrapy.downloadermiddlewares.DownloaderMiddleware
-
process_request(request, spider)
當request通過這個
Downloader Middleware
時咧最,將會調(diào)用這個函數(shù)來處理request捂人。參數(shù):
-
request(
Request
對象) - 被處理的request御雕。 -
spider(
Spider
對象) - 這個reuqest所對應(yīng)的spider。
這個方法應(yīng)該返回以下四種結(jié)果之一:
-
None
如果返回None滥搭,那么
Scrapy
會繼續(xù)往下處理這個request酸纲,執(zhí)行接下來的其他Middleware
,最終下載這個request并獲得response瑟匆。 -
Response
如果返回的是Response的話闽坡,那么
Scrapy
會中斷對這個Request的后續(xù)處理,不再調(diào)用后續(xù)的process_request()
和process_exception()
愁溜。Scrapy
會調(diào)用所有啟用的Middleware
中的process_response()
來處理這個Response疾嗅。 -
Request
如果返回的是Request,那么
Scrapy
同樣會中斷這個Request的后續(xù)處理冕象,然后把返回的Request重新進行調(diào)度代承。 -
IgnoreRequest
如果在這個方法中拋出了一個
IgnoreRequest
異常,那么啟用的Middleware
中的process_exception()
將會被調(diào)用渐扮。如果沒有一個方法處理這個異常论悴,那么Request自帶的異常處理函數(shù)(Request.errback
)會被調(diào)用。如果沒有任何代碼處理這個異常墓律,那么這個Request將被忽略并且不會被記錄膀估。
-
request(
-
process_response(request, response, spider)
當
Downloader
生成的Response在通過Downloader
時,用來處理Response的方法耻讽。參數(shù):
-
request(
Request
對象) - response所對應(yīng)的request察纯。 -
response(
Response
對象) - 要處理的response。 -
spider(
Spider
對象) - 這個response所對應(yīng)的spider针肥。
這個方法應(yīng)該返回以下三種結(jié)果之一:
-
Response
如果返回的是Response饼记,那么
Scrapy
將會繼續(xù)調(diào)用下一個Middleware
中的process_response()
方法進行處理。 -
Request
如果返回的是Request祖驱,那么
Scrapy
將會中斷對Response的處理握恳,并將返回的Request重新進行調(diào)度瞒窒。與process_request()
中返回Request的處理方式是一樣的捺僻。 -
IgnoreRequest
如果在方法中拋出了一個
IgnoreRequest
異常,那么Request自帶的異常處理函數(shù)(Request.errback
)會被調(diào)用崇裁。如果沒有任何代碼處理這個異常匕坯,那么這個Request將被忽略并且不會被記錄。
-
request(
-
process_exception(request, exception, spider)
如果在Download或者
process_request()
過程中拋出一個異常拔稳,那么將會調(diào)用此方法來處理異常葛峻。參數(shù):
-
request(
Request
對象) - 產(chǎn)生異常的request。 -
exception(
Exception
對象) - 拋出的異常巴比。 -
spider(
Spider
對象) - request對應(yīng)的Spider
术奖。
這個方法應(yīng)該返回以下三種結(jié)果之一:
-
None
如果返回的是None礁遵,
Scrapy
將會繼續(xù)調(diào)用啟用的Middleware
中的process_exception()
來處理異常。當所有剩下的process_exception()
都調(diào)用完畢后采记,會使用默認的異常處理佣耐。 -
Response
如果返回的是一個
Response
對象,那么Scrapy
將會中斷異常的處理唧龄,重新調(diào)用所有啟用的Middleware
中的process_response()
來處理返回的這個Response
兼砖。 -
Request
如果返回的是一個
Request
對象,那么Scrapy
將會中斷異常的處理既棺,重新調(diào)用所有啟用的Middleware
中的process_request()
來處理返回的這個Request
讽挟。
-
request(
爬蟲中間件(Spider Middleware)
爬蟲中間件與下載中間件的作用都是對經(jīng)過的數(shù)據(jù)進行處理,只不過爬蟲中間件的位置處于Spider
與引擎之間丸冕,主要處理Spider
的輸入和輸出耽梅。相對于下載中間件來說,爬蟲中間件使用的較少胖烛。
爬蟲中間件(Spider Middleware
)與下載中間件(Downloader Middleware
)最主要的區(qū)別是褐墅,下載中間件處理的是進出Downloader
的Request和Response,爬蟲中間件處理的洪己,主要是從Downloader
返回的Response和Spider
生成的Request和Item妥凳。
可以看到,在處理Response這里答捕,下載中間件和爬蟲中間件是有一定的功能重疊的逝钥。但是在處理Request的時候,兩者之間其實還間隔著一個調(diào)度器(Scheduler
)拱镐,所以這里處理的Request還有著未經(jīng)調(diào)度器處理艘款,和已經(jīng)經(jīng)過調(diào)度器處理的區(qū)別。并且沃琅,爬蟲中間件還能處理Spider
生成的Item哗咆。
爬蟲中間件同樣有一部分已經(jīng)默認啟用的基礎(chǔ)中間件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,
}
啟用爬蟲中間件的方式也和下載中間件基本一致。
SPIDER_MIDDLEWARES = {
'myproject.middlewares.CustomSpiderMiddleware': 543,
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None,
}
自定義爬蟲中間件(Spider Middleware)
每個爬蟲中間件都是一個類:
class scrapy.spidermiddlewares.SpiderMiddleware
其中定義了以下一個或多個方法:
-
process_spider_input(response, spider)
這個方法將會在Response傳入
Spider
之前調(diào)用益眉,來處理這個Response晌柬。參數(shù):
-
response(
Response
對象) - 處理的response。 -
spider(
Spider
對象) - response對應(yīng)的spider郭脂。
process_spider_input()
需要返回一個None或者拋出一個異常年碘。-
None
如果返回的是None的話,
Scrapy
將會調(diào)用接下來的Middleware
繼續(xù)處理Response展鸡。 -
拋出異常
如果這個方法拋出的是一個異常的話屿衅,
Scrapy
將會停止處理這個Response。并調(diào)用Request對應(yīng)的異常處理函數(shù)莹弊。
-
response(
-
process_spider_output(response, result, spider)
這個方法會在
Spider
返回結(jié)果時調(diào)用涤久,用來處理Spider
返回的Request涡尘,dict或者Item。參數(shù):
-
response(
Response
對象) -Spider
用來生成返回結(jié)果的Response响迂。 -
result(
Request
,dict
或者Item
對象) -Spider
生成的返回結(jié)果悟衩。 -
spider(
Spider
對象) - 處理結(jié)果的Spider
。
-
response(
-
process_spider_exception(response, exception, spider)
在
Spider
或者process_spider_input()
中拋出異常時栓拜,將會調(diào)用此方法來處理異常座泳。參數(shù):
-
response(
Response
對象) - 異常拋出時被處理的Response。 -
exception(
Exception
對象) - 拋出的異常幕与。 -
spider(
Spider
對象) - 拋出異常的Spider
挑势。
這個方法返回的結(jié)果有兩種選擇:
-
None
如果返回的是None,那么
Scrapy
將會繼續(xù)調(diào)用接下來的Midleware
中的process_spider_exception()
方法來處理異常啦鸣。 -
包含Request潮饱,dict或者Item的可迭代對象
如果返回的是一個包含Request,dict或者Item的可迭代對象诫给,那么異常處理將會被中斷香拉。轉(zhuǎn)而開始調(diào)用
process_spider_output()
方法。
-
response(
-
process_start_requests(start_requests, spider)
當
Spider
訪問的是最初始的start_requests
時中狂,將會調(diào)用這個方法來進行處理Spider
的輸出凫碌,這個方法的使用與process_spider_output()
大致相同,區(qū)別只是處理的目標不同胃榕。而且這個方法只能返回一個包含Request的可迭代對象盛险。
系列文章: