Scrapy入門到放棄04:下載器中間件,讓爬蟲更完美


前言

MiddleWare,顧名思義,中間件。主要處理請求(例如添加代理IP、添加請求頭等)和處理響應

本篇文章主要講述下載器中間件的概念,以及如何使用中間件和自定義中間件。

MiddleWare分類

依舊是那張熟悉的架構圖。

Scrapy架構

從圖中看,中間件主要分為兩類:

  1. Downloader MiddleWare:下載器中間件
  2. Spider MiddleWare:Spider中間件

本篇文主要介紹下載器中間件,先看官方的定義:

下載器中間件是介於Scrapy的request/response處理的鈎子框架。 是用於全局修改Scrapy request和response的一個輕量、底層的系統。

作用

如架構圖中所描述的一樣,下載器中間件位於engine和下載器之間。engine將未處理的請求發送給下載器的時候,會經過下載器中間件,這時候在中間件里可以包裝請求,例如修改請求頭信息(設置UA、cookie等)和添加代理IP。

當下載器將網站的響應發送給engine的時候,也會經過下載器中間件,這里我們就可以對響應內容進行處理。

內置下載器中間件

Scrapy內置了很多下載器中間件供開發者使用。當我們啟動一個Scrapy爬蟲時,Scrapy會自動幫助我們啟用這些中間件。如圖:

內置中間件

圖中就是在啟動Scrapy程序時控制台打印的日志信息,我們發現Scrapy幫我們啟用了很多下載器中間件和Spider中間件。

這里,先看看這些內置的中間件是如何發揮作用的?

RetryMiddleware

其實,這些內置中間件是和settings中的配置配套使用的。這里就拿RetryMiddleware為例。它的作用主要是:當請求失敗時,可以根據RETRY_ENABLEDRETRY_TIMES配置來啟用重試策略以及決定重試次數。就醬!!

那么問題又來了,這么多中間件,我去哪里找這個settings配置和中間件的對應關系啊??

這里我的方法有兩種:

  1. 去官方文檔,上篇文章有鏈接
  2. 看源碼注釋,在scrapy包下的都有中間件對應的py文件

RetryMiddleware

注釋里面寫的明明白白,代碼中獲取的參數也一覽無余。

自定義中間件

有時候,內置的中間件滿足不了自己的需求,所以我們就要自力更生,自定義中間件。所有的中間件都在middlewares.py中進行定義。

我們打開middlewares.py,發現里面已經自動生成了一個下載器中間件和Spider中間件。

先看自生成的下載器中間件模板:

下載器中間件

可以看到里面主要有五個方法:

  1. from_crawler:類方法,用於初始化中間件
  2. process_request:每個request通過下載中間件時,都會調用該方法,對應架構圖步驟4
  3. process_response:處理下載器返回的響應內容,對應架構圖步驟7
  4. process_exception:當下載器或者處理請求異常時,調用此方法
  5. spider_opened:內置的信號量回調方法,這里先不關注,先不關注!

這里主要關注3,順帶了解一下4、5。

process_request()

此方法有兩個參數:

  1. request:spider發起的需要處理的request
  2. spider:該request對應的spider,暫定信號量細講這個對象
def process_request(self, request, spider):
        # Called for each request that goes through the downloader middleware.

        # Must either:
        # - return None: continue processing this request
        # - or return a Response object
        # - or return a Request object
        # - or raise IgnoreRequest: process_exception() methods of
        #   installed downloader middleware will be called
        return None

這里主要是為了讓大家看注釋,看注釋的目的是為了告訴大家:此方法必須返回值

  1. None:基本上用的都是這個返回值。表示這個請求可以進去下一個中間件進行處理了。
  2. request:停止調用process_request方法,並重新將request放回隊列重新調度
  3. response:不會調用其他的 process_request,直接返回response,執行process_response。

還有一個是raise拋出異常,其實基本上返回值都用None,其他的目前可以僅做了解,有興趣的可以自己探索一下。

process_response()

此方法有三個參數:

  1. request:response所對應的request
  2. response:被處理的response
  3. spider:response所對應的spider
def process_response(self, request, response, spider):
        # Called with the response returned from the downloader.

        # Must either;
        # - return a Response object
        # - return a Request object
        # - or raise IgnoreRequest
        return response

一樣是看注釋,返回值有兩個:

  1. response:下載器返回的響應內容,在各個中間件的process_response處理
  2. request:停止調用process_response方法,響應不會到達spider,並重新將request放回隊列重新調度

這里記住,只要return response就行。

process_exception()

def process_exception(self, request, exception, spider):
        # Called when a download handler or a process_request()
        # (from other downloader middleware) raises an exception.

        # Must either:
        # - return None: continue processing this exception
        # - return a Response object: stops process_exception() chain
        # - return a Request object: stops process_exception() chain
        pass

此方法就是當上面兩個方法拋出異常的時候就會進入此方法,返回值有三個,意思和上面的差不多,用None就行。

啟用和禁用中間件

自定義的中間件,有時候會和內置中間件功能重復,也擔心功能上互相覆蓋。所以這里我們可以選擇,在配置中關掉內置中間件。

我個人比較喜歡自定義User-Agent中間件,但是Scrapy內置UserAgentMiddleware中間件,這就沖突了。如果內置中間件執行優先級低,后執行的話,則內置的UA就會覆蓋自定義的UA。所以,我們需要關掉這個內置中UA中間件。

DOWNLOADER_MIDDLEWARES參數用來設置下載器中間件。其中,Key為中間件路徑,Value為中間件執行優先級,數字越小,越先執行,當Value為None時,表示禁用。

# settings.py
DOWNLOADER_MIDDLEWARES = {
    # 禁用默認的useragent插件
    'scrapy.downloadermiddleware.useragent.UserAgentMiddleware': None,
    # 啟用自定義的中間件
    'ScrapyDemo.middlewares.VideospiderDownloaderMiddleware': 543,
}

這樣,內置的UA中間件則被禁用。

調用優先級

其次我們要明確的是:中間件是鏈式調用,一個請求會根據中間件的優先級,先后經過每個中間件,響應也是

流程圖

上面也說了,每個中間件都會設置一個執行優先級,數字越小越先執行。例如中間件1的優先級設置為200,中間件2的優先級設置為300。

當spider發起一個請求時,request會先經過中間件1的process_request進行處理,然后到達中間件2的此方法進行處理,當經過所有的中間件的此方法處理之后,最后到達下載器進行網站請求,然后返回響應內容。

process_response就是逆序處理,先到達中間件2的此方法,再到達中間件1,最后響應返回spider中,由開發者處理。

實踐

這里我們自定義一個下載器中間件,來添加User-Agent。

自定義中間件

在middlewares.py中定義一個中間件:

class CustomUserAgentMiddleWare(object):

    def process_request(self, request, spider):
        request.headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36'
        return None

    def process_response(self, request, response, spider):
        print(request.headers['User-Agent'])
        return response

啟用中間件

為了直觀,我們不修改settings.py全局配置,依舊使用代碼內局部配置。

import scrapy

class DouLuoDaLuSpider(scrapy.Spider):
    name = 'DouLuoDaLu'
    allowed_domains = ['v.qq.com']
    start_urls = ['https://v.qq.com/detail/m/m441e3rjq9kwpsc.html']

    custom_settings = {
        'DOWNLOADER_MIDDLEWARES': {
            # 禁用默認的useragent插件
            'scrapy.downloadermiddleware.useragent.UserAgentMiddleware': None,
            # 啟用自定義的中間件
            'ScrapyDemo.middlewares.CustomUserAgentMiddleWare': 400
        }
    }


    def parse(self, response):
        pass

這里首先禁用了默認的UA中間件,然后啟用了自定義的UA中間件。並且我在最后一行打上斷點,Debug看UA是否設置成功。

測試結果

Debug模式啟動程序,這里先把自定義的UA中間件禁用。

禁用

如圖,request的UA是Scrapy。我們將注釋去掉,啟動UA中間件,再次啟動程序測試。

啟用

如圖,request的UA已經變成我在中間件中設置的UA了。

設置代理IP

依舊是在process_request方法中設置代理IP。

代碼如下:

request.meta["proxy"] = 'http://ip:port'

結語

下載器中間件主要的功能還是包裝請求,我個人自定義下載器中間件都是用來動態設置UA和實時檢測更換代理IP。至於其他的場景需求,內置的下載器中間件基本上夠用。

當然,不去學習下載器中間件這一塊的知識同樣可以開發Scrapy爬蟲,但是下載器中間件會讓你的爬蟲更加完美。

本來想把下載器中間件和Spider中間件寫在一篇中,但是知識點太碎,不好排版,而且還容易混淆,所以Spider中間件就留在下一篇寫,期待下一次相遇。



95后小程序員,寫的都是日常工作中的親身實踐,置身於初學者的角度從0寫到1,詳細且認真。文章會在公眾號 [入門到放棄之路] 首發,期待你的關注。

感謝每一份關注


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM