亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

Scrapy 中的中間件

今天我們來聊一聊 Scrapy 框架中的中間件使用,包括 Spider 中間件、下載中間件等。它屬于 Scrapy 框架的一個重要部分,是我們定制化 Scrapy 框架時的重要基礎。

1. Spider 中間件

1.1 Spider 中間件介紹

圖片描述

Scrapy架構設計圖

我們從架構圖中可以看到,Spider 中間件(Spider Middlewares)位于引擎模塊和 Spiders 中間,通過這個中間件模塊我們可以控制發送給 Spider 的 Response 以及 Spider 傳回給引擎的 Items 和 Requests。使用 Spider 中間件的第一步是要在配置文件中啟用它,啟用方式只需要設置 SPIDER_MIDDLEWARES 的值即可:

SPIDER_MIDDLEWARES = {
    # 指定編寫的Spider中間件類的位置
    'myproject.middlewares.CustomSpiderMiddleware': 543,
}

自定義在 settings.py 中的 Spider 中間件會和 Scrapy 內置的 SPIDER_MIDDLEWARES_BASE 設置合并,然后根據對應的 value 值進行排序,得到最終的 Spider 中間件的有序執行列表:值最小的最靠近引擎那一側,值最大的最靠近 Spider。

我們來看 Scrapy 內置的 SPIDER_MIDDLEWARES_BASE 值如下所示。上面設置的 543 正好使得自定義的 CustomSpiderMiddleware 中間件位于 OffsiteMiddleware 中間件和 RefererMiddleware 中間件之間。

# 源碼位置:scrapy/settings/default_settings.py

SPIDER_MIDDLEWARES_BASE = {
    # Engine side
    '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 side
}

當我們想禁止相應的 Spider 中間件時,只需要在 SPIDER_MIDDLEWARES 中將對應的中間件的值設置為 None 即可:

SPIDER_MIDDLEWARES = {
    'myproject.middlewares.CustomSpiderMiddleware': 543,
    'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None,
}

1.2 編寫 Spider 中間件

編寫 Scrapy 中自定義的 Spider 中間件非常簡單,每個中間組件都是實現了以下一個或者多個方法的 Python 類:

  • process_spider_input(response, spider):對于通過 Spider 中間件并進入 Spider 進行處理的每個 response,都會調用此方法。該方法返回 None 或者拋出異常,如果返回的是 None,Scrapy 將繼續處理這個響應,執行所有其他的 Spider 中間件,直到最后,響應被交給 Spider 進行處理;如果是拋出了一個異常,則會調用下面的 process_spider_exception() 方法處理而不會在調用其他的 Spider 中間件;
  • process_spider_output(response, result, spider):這個方法一般會在 Spider 處理完響應后調用,也就是我們前面編寫的 parse() 或者自定義的 parse_xxx() 方法執行完后調用;
  • process_spider_exception(response, exception, spider):當 spider 或者 上一個 spider 中間件的 process_spider_output() 拋出異常時會調用該方法;
  • process_start_requests(start_requests, spider):此方法與 spider 的開始請求一起調用,其工作方式與 process_spider_output() 方法類似,只不過它沒有關聯的 response,且只返回請求 (而不是 items)。

注意:我們一般實現的自定義中間件類會寫在 scrapy 項目的 middlewares.py 中,當然也可以寫到任意的代碼文件中,只需要在寫入配置時指定好完整的類路徑即可。

1.3 Scrapy 中內置的 Spider 中間件

接下來,我們來看看 Scrapy 中內置的 Spider 中間件,目前最新的 Scrapy-2.2.0 中一共有 5 個內置的 Spider 中間件:

圖片描述

Scrapy內置的Spider中間件

我們來分別介紹下這 5 個內置的 Spider 中間件,如果能利用好這些中間件以及進行合理的配置,可以簡化不少代碼和提高網站爬取的成功率和效率。

DepthMiddleware

爬取深度中間件,該中間件用于追蹤被爬取網站中每個 Request 的爬取深度。通過對該中間件相關參數的設置,可以限制爬蟲爬取的最大深度,且可以根據深度控制請求的優先級等。該中間件對應的參數設置有:

  • DEPTH_LIMiT:允許爬取的最大深度,如果為0,則不限制;
  • DEPTH_STATS:是否收集爬取深度統計數據;
  • DEPTH_PRIORITY:是否根據 Request 深度對其安排相應的優先級進行處理。

HttpErrorMiddleware

該中間件的作用是過濾掉所有不成功的 HTTP 響應,但這會增加開銷,消耗更多的資源,并使 Spider 邏輯更加復雜;

根據 HTTP 標準,響應碼在 200~300 之間都是成功的響應。如果想處理這個范圍之外的 Response,可以通過 Spider 的 handle_httpstatus_list 屬性值或者配置文件中的 HTTPERROR_ALLOWED_CODES 值來指定 Spider 能處理的 Response 狀態碼:

class MySpider(CrawlSpider):
    handle_httpstatus_list = [404]

例如上面這樣的寫法就是只處理響應碼為 404 的 Response。此外 HttpErrorMiddleware 中間件的配置值有兩個,分別為:

  • HTTPERROR_ALLOWED_CODES:默認值為[],傳遞此列表中包含的非200狀態代碼的所有響應;
  • HTTPERROR_ALLOW_ALL:默認為 False,傳遞所有的 Response而不考慮其狀態響應碼的值。

OffsiteMiddleware

過濾請求中間件。用于過濾掉所有 Spider 覆蓋主機域名外的 URL 請求;

RefererMiddleware

位置參考中間件。它的作用是根據生產的 Response 的 URL 來填充 Request Referer 信息;

UrlLengthMiddleware

網址長度限制中間件。它用于過濾 URL 長度大于 URLLENGTH_LIMIT 的值得 Request;

scrapy/settings/default_settings.py 中我們可以看到這樣的配置:

SPIDER_MIDDLEWARES_BASE = {
    # Engine side
    '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 side
}

這些 Spider 中間件都默認啟用,且從 Engine 端到 Spider 端的順序如上配置所示。如果想禁止某個內置的 Spider 中間件,我們直接在 settings.py 文件中將該 Spider 的值設置為 None 即可,示例如下:

# 配置位置: 爬蟲項目/settings.py

SPIDER_MIDDLEWARES = {
    'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None,
}

2. 下載中間件

2.1 下載中間件介紹

從前面的 Scrapy 架構圖圖中可知,下載中間件(Downloader Middlewares)是位于 Scrapy 引擎(Scrapy Engine)和下載器(DownLoader)之間的,用于處理請求以及響應的中間層。

它可以全局修改 Scrapy 的請求和響應。那么如何激活下載中間件呢?假設我們編寫了一個中間件類:我們只需要將其加入全局配置文件 (settings.py) 中即可:

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.CustomDownloaderMiddleware': 543,
}

DOWNLOADER_MIDDLEWARES 中的 key 就是定義的下載中間件類,而 value 就是控制該中間件執行順序的值,可以簡單理解為表示該中間件執行的優先級。

值越小的越靠近 Scrapy 引擎,值越大越靠近下載器。我們在自定義下載中間件時,也需要考慮給自定義的下載中間件設置合理的值。因此,我們先看看 Scrapy 框架默認啟用的下載中間件情況:

# 源碼位置: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
}
# ...

如何不想啟動默認的某個內置中間件時,同樣只需要在 settings.py 中覆蓋其值并設置為 None,即可禁用該下載中間件:

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.CustomDownloaderMiddleware': 543,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
}

了解了這些后,我們來看看如何編寫自定義的下載中間件。

2.2 編寫下載中間件

編寫自定義的下載中間件也非常簡單,只需要在自定義的類中實現一個或多個特定名字的方法即可?,F在來介紹下下載中間件中那些特定方法:

process_request(request, spider)

當 Request 請求經過下載中間件時會調用該方法。該方法只能返回 None、Response 對象、Request 對象或者 IgnoreRequest 異常的其中之一。如果返回 None,Scrapy 將執行其他中間件中相應的方法繼續處理該 Request,直到該 Request 被下載器的處理函數處理;如果返回 Response 對象,則 Scrapy 將直接返回該 Response,而不再繼續調用原鏈路上的其他中間件的 process_request(),process_exception()或相應的下載方法,但是會依次調用已啟用的中間件的 process_request() 方法;

process_response(request, response, spider)

在請求的 Response 經過下載中間件時會調用該方法。該方法返回 Response 對象、Request 對象或者是拋出 IgnoreRequest 異常。如果返回的是 Response 對象,則該 Response 會被其他中間件的 process_response() 方法處理;如果其返回的是一個 Request 對象,那么其余的下載中間件將不會處理,返回的 Request 會被引擎重新調度去下載;如果是拋出異常,則調用 Request.errback。如果沒有相應的代碼處理該異常,則忽略該異常;

process_exception(request, exception, spider)

在下載處理器或者下載中間件的 process_request() 方法拋出異常時,Scrapy 將調用該方法進行處理。該方法必須返回為 None、Response 對象以及 Request 對象三者之一;

以上這些關于下載中間件的特定函數的輸入和輸出信息我們可以在官方文檔中找到詳細的解答。上面的介紹主要是翻譯了官方文檔對這些方法的說明。后面我們也會在學習源碼中找到這些輸出輸出的邏輯。

2.3 內置的下載中間件

同樣,在 Scrapy 中為我們內置了不少的下載中間件,可以方便地配置下載參數,比如 Cookie、代理等。我們現在來介紹一些常用的下載中間件。

CookiesMiddleware:該中間件主要用于給請求加上 Cookie,這樣可以方便我們的爬蟲程序使用 Cookie 去訪問網站。它記錄了向 Web Server 發送的 Cookie,并在之后的 Request 請求中帶上該 Cokkile,就像我們操作瀏覽器那樣。該中間件在 settings.py 中的配置有2個:

  • COOKIES_ENABLE:默認為 True,表明啟用 cookies 中間件,如果為 False,則不會使用 cookies。

    Tips:如果 Request.meta 參數的 dont_merge_cookies 的值為 True,那么無論 COOKIES_ENABLE 指定為何值,cookies 在這個請求的來回中都不會做任何處理;

  • COOKIES_DEBUG:默認為 False。如果為 True,則會記錄所有請求發送的 cookies 和響應接收到的 cookies;

HttpProxyMiddleware:該中間件通過在 Request.meta 中添加 proxy 屬性值為該請求設置 HTTP 代理;

HttpCacheMiddleware:該中間件為所有 HTTP 請求和響應提供 low-level 緩存,它需要和緩存存儲后端以及緩存的策略相結合;

DefaultHeadersMiddleware:該中間件通過配置文件中 DEFAULT_REQUEST_HEADERS 的值來設置所有請求默認的請求頭;

DownloadTimeoutMiddleware:該中間件主要用來設置下載超時時間。對應 settings.py 中的配置值為:DOWNLOAD_TIMEOUT或者 spider 的 download_timeout 屬性

好了,常用的下載中間件就介紹這么多了,其余的可以繼續參考官方文檔,寫的非常詳細。

3. 小結

本小節中分別從三個角度介紹了 Scrapy 中的 Spider 中間件和 下載中間件:中間件介紹、如何編寫相應的中間件以及 Scrapy 內置的相應中間件。在理解了這些后,我們在下面兩節會使用一個例子來對前面學到的知識進行實戰演練,達到即學即用的目的。

圖片描述