Scrapy框架——中間件詳解


廢話不多說,兩個中間件,一個下載器,另一個Spider


  • 下載器中間件

    位於Scrapy引擎和下載器之間,主要用來處理從EGINE傳到DOWLOADER的請求request,已經從DOWNLOADER傳到EGINE的響應response,你可用該中間件做以下幾件事
    官方英文文檔:

    process a request just before it is sent to the Downloader (i.e. right
    before Scrapy sends the request to the website); change received
    response before passing it to a spider; send a new Request instead of
    passing received response to a spider; pass response to a spider
    without fetching a web page; silently drop some requests.

    簡單點理解就是說,引擎engine將request對象交給下載器之前,會經過下載器中間件;此時,中間件提供了一個方法 process_request,可以對 request對象進行設置隨機請求頭、IP代理、Cookie等;當下載器完成下載時,獲得到 response對象,將它交給引擎engine的過程中,再一次經過 下載器中間件;此時,中間件提供了另一個方法 process_response;可以判斷 response對象的狀態碼,來決定是否將 response提交給引擎。

    當然可以自己寫中間件,想要寫明白,就需要搞懂以下這三個方法。

    1. process_request(request, spider)
    2. process_response(request, response, spider)
    3. process_exception(request, exception, spider)

  • 方法1:process_request(request,spider)

    當每個request通過下載中間件時,該方法被調用。

    process_request() 必須返回其中之一: 返回 None 、返回一個 Response 對象、返回一個 Request 對象或raise 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 process_response(request, response, spider)

    同樣的,返回值一樣很重要:

    process_request() 必須返回以下之一: 返回一個 Response 對象、 返回一個 Request 對象或raise一個 IgnoreRequest 異常。

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

    • 如果其返回一個 Request 對象,則中間件鏈停止, 返回的request會被重新調度下載。處理類似於 process_request() 返回request所做的那樣。

    • 如果其拋出一個 IgnoreRequest 異常,則調用request的errback(Request.errback)。 如果沒有代碼處理拋出的異常,則該異常被忽略且不記錄(不同於其他異常那樣)。


  • 方法3 process_exception(request, exception, spider)

    返回值

    process_exception() 應該返回以下之一: 返回 None 、 一個 Response 對象、或者一個 Request 對象。

    • 如果其返回 None ,Scrapy將會繼續處理該異常,接着調用已安裝的其他中間件的 process_exception() 方法,直到所有中間件都被調用完畢,則調用默認的異常處理。

    • 如果其返回一個 Response 對象,則已安裝的中間件鏈的 process_response() 方法被調用。Scrapy將不會調用任何其他中間件的 process_exception() 方法。

    • 如果其返回一個 Request 對象, 則返回的request將會被重新調用下載。這將停止中間件的 process_exception() 方法執行,就如返回一個response的那樣。


熟悉了這三個方法,基本上可以寫出,UA、Cookie、代理、Selenium中間件了。想要更進一步,那麼需要對 Scrapy內置的下載器中間件有所掌握。

內置的BASE 下載器中間件如下:

{
    'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 100,
    
    'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware': 300,
    
    'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware': 350,
    
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 400,
    
    'scrapy.downloadermiddlewares.retry.RetryMiddleware': 500,
    
    'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware': 550,
    
    '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.chunked.ChunkedTransferMiddleware': 830,
    
    'scrapy.downloadermiddlewares.stats.DownloaderStats': 850,
    
    'scrapy.downloadermiddlewares.httpcache.HttpCacheMiddleware': 900,
}

按照優先級來介紹:

  1. RobotsTxtMiddleware

    該中間件過濾所有robots.txt eclusion standard中禁止的request。

    確認該中間件及 ROBOTSTXT_OBEY 設置被啓用以確保Scrapy尊重robots.txt。

    主要看你,settings文件中 ROBOTSTXT_OBEY = False 是否設置了 False

  2. HttpAuthMiddleware

    該中間件完成某些使用 Basic access authentication (或者叫HTTP認證)的spider生成的請求的認證過程。

  3. DownloadTimeoutMiddleware

    該中間件設置 DOWNLOAD_TIMEOUT 指定的request下載超時時間.

    這個中間件,我用過。繼承它之後,重寫方法,實現對代理IP訪問慢,設置一個超時時間,對它進行判斷,超過改時間,進行更換proxy。

  4. UserAgentMiddleware

    用於覆蓋spider的默認user agent的中間件。

    要使得spider能覆蓋默認的user agent,其 user_agent 屬性必須被設置。

    經常使用之一,你可以選擇繼承該中間件,也可以自己寫一個。它們之間不是覆蓋的關係,而是合併。

  5. RetryMiddleware

    該中間件將重試可能由於臨時的問題,例如連接超時或者HTTP 500錯誤導致失敗的頁面。
    爬取進程會收集失敗的頁面並在最後,spider爬取完所有正常(不失敗)的頁面後重新調度。 一旦沒有更多需要重試的失敗頁面,該中間件將會發送一個信號(retry_complete), 其他插件可以監聽該信號。

  6. DefaultHeadersMiddleware

    該中間件設置 DEFAULT_REQUEST_HEADERS 指定的默認request header。

  7. MetaRefreshMiddleware

    該中間件根據meta-refresh html標籤處理request重定向。

  8. HttpCompressionMiddleware

    該中間件提供了對壓縮(gzip, deflate)數據的支持

  9. RedirectMiddleware
    該中間件根據response的狀態處理重定向的request。通過該中間件的(被重定向的)request的url可以通過 Request.meta 的 redirect_urls 鍵找到。

  10. CookiesMiddleware

    參見博客 CookieMiddleWare詳解

  11. HttpProxyMiddleware
    該中間件提供了對request設置HTTP代理的支持。您可以通過在 Request 對象中設置 proxy 元數據來開啓代理。

  12. ChunkedTransferMiddleware
    該中間件添加了對 chunked transfer encoding 的支持。

  13. DownloaderStats

    pass

  14. HttpCacheMiddleware

    該中間件爲所有HTTP request及response提供了底層(low-level)緩存支持。 其由cache存儲後端及cache策略組成。

說了這麼多內置中間件,可以禁用某些中間件嗎? 當然可以

假如,想禁用 RetryMiddleware中間件,只需要在settings文件種的中間件字典里加入, ‘scrapy.downloadermiddlewares.retry.RetryMiddleware’: None,

說了這麼多,還不如一張圖來的清晰明瞭


  • Spider 中間件

    Spider Middleware是介入到Scrapy的Spider處理機制的鉤子框架。如以下經典 scrapy流程圖:

    Spider Middleware有如下三個作用:

    1. 我們可以在Downloader生成的Response發送給Spider之前,也就是在Response發送給Spider之前對Response進行處理。
    2. 我們可以在Spider生成的Request發送給Scheduler之前,也就是在Request發送給Scheduler之前對Request進行處理。
    3. 我們可以在Spider生成的Item發送給Item Pipeline之前,也就是在Item發送給Item Pipeline之前對Item進行處理。
    • 同樣的,想要寫好 Spider中間件,需要掌握以下四個方法

      • process_spider_input(response, spider)

      • process_spider_output(response, result, spider)

      • process_spider_exception(response, exception, spider)

      • process_start_requests(start_requests, spider)

        具體如何使用請看----》》》 spider中間件使用

說實話,Spider Middleware使用的頻率不高,使用的絕大部分都是下載器中間件。這裏,我再舉幾個自定義下載器中間件的例子。


1. 設置隨機UA

一般有兩種方法,一是繼承 scrapy提供的內置 UserAgentMiddleware

使用 fake_useragent 庫 可以隨機產生一個UA,不錯的一個庫。最後,記得在settings配置文件中添加該中間件

法1:

# author:dayin
# Date:2019/12/17 0017
# UserAgentMiddleWare.py

from fake_useragent import UserAgent

class UserAgentMiddleWare(object):
    ua = UserAgent()
    def process_request(self, request, spider):
        request.meta['User-Agent'] = self.ua.random

法2:

class UserAgentMiddleWare2(UserAgentMiddleware):
    ua = UserAgent()

    def process_request(self, request, spider):
        request.meta['User-Agent'] = self.ua.random

2. 設置IP代理


# author:dayin
# Date:2019/12/17 0017
import json
import requests
class ProxyMiddleWare(object):
    def process_request(self, request, spider):
        request.meta['proxy'] = self.getIP()
        print('當前的代理ip爲....', request.meta['proxy'])

    @staticmethod
    def getIP():
        ip_port = json.loads(requests.get('http://127.0.0.1:5555/get').json())['proxy']
        return 'http://' + ip_port

    def process_exception(self, request, spider, exception):
        print('----' * 100)
        print('代理失效了,換一個代理IP')
        request.meta['proxy'] = self.getIP()
        print('----' * 100)
        return request.replace(dont_filter=True)

我在本地端口5555設置了一個IP代理池,訪問它可以返回一個代理IP


end…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章