日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

第44讲:scrapy中间键Middleware的使用

發布時間:2024/4/11 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第44讲:scrapy中间键Middleware的使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們在 Scrapy 架構中,可以看到有一個叫作 Middleware 的概念,中文翻譯過來就叫作中間件,在 Scrapy 中有兩種 Middleware,一種是 Spider Middleware,另一種是 Downloader Middleware,本節課我們分別來介紹下。

1.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 進行處理。

使用說明

需要說明的是,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 的。

1.1 核心方法

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 個方法的詳細用法。

1.1.1 process_spider_input(response, spider)

當 Response 通過 Spider Middleware 時,該方法被調用,處理該 Response。

方法的參數有兩個:

  • 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() 來處理。

1.1.2 process_spider_output(response, result, spider)

當 Spider 處理 Response 返回結果時,該方法被調用。

方法的參數有三個:

  • response,即 Response 對象,即生成該輸出的 Response;

  • result,包含 Request 或 Item 對象的可迭代對象,即 Spider 返回的結果;

  • spider,即 Spider 對象,即其結果對應的 Spider。

process_spider_output() 必須返回包含 Request 或 Item 對象的可迭代對象。

1.1.3 process_spider_exception(response, exception, spider)

當 Spider 或 Spider Middleware 的 process_spider_input() 方法拋出異常時, 該方法被調用。

方法的參數有三個:

  • 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() 將不會被調用。

1.1.4 process_start_requests(start_requests, spider)

該方法以 Spider 啟動的 Request 為參數被調用,執行的過程類似于 process_spider_output() ,只不過其沒有相關聯的 Response 并且必須返回 Request。

方法的參數有兩個:

  • start_requests,即包含 Request 的可迭代對象,即 Start Requests;

  • spider,即 Spider 對象,即 Start Requests 所屬的 Spider。

其必須返回另一個包含 Request 對象的可迭代對象。

2.Downloader Middleware 的用法

Downloader Middleware 即下載中間件,它是處于 Scrapy 的 Request 和 Response 之間的處理模塊。

Scheduler 從隊列中拿出一個 Request 發送給 Downloader 執行下載,這個過程會經過 Downloader Middleware 的處理。另外,當 Downloader 將 Request 下載完成得到 Response 返回給 Spider 時會再次經過 Downloader Middleware 處理。

也就是說,Downloader Middleware 在整個架構中起作用的位置是以下兩個。

  • 在 Scheduler 調度出隊列的 Request 發送給 Downloader 下載之前,也就是我們可以在 Request 執行下載之前對其進行修改。

  • 在下載后生成的 Response 發送給 Spider 之前,也就是我們可以在生成 Resposne 被 Spider 解析之前對其進行修改。

Downloader Middleware 的功能十分強大,修改 User-Agent、處理重定向、設置代理、失敗重試、設置 Cookies 等功能都需要借助它來實現。下面我們來了解一下 Downloader Middleware 的詳細用法。

2.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 都可以定義 process_request() 和 request_response() 方法來分別處理請求和響應,對于 process_request() 方法來說,優先級數字越小越先被調用,對于 process_response() 方法來說,優先級數字越大越先被調用。

如果自己定義的 Downloader Middleware 要添加到項目里,DOWNLOADER_MIDDLEWARES_BASE 變量不能直接修改。Scrapy 提供了另外一個設置變量 DOWNLOADER_MIDDLEWARES,我們直接修改這個變量就可以添加自己定義的 Downloader Middleware,以及禁用 DOWNLOADER_MIDDLEWARES_BASE 里面定義的 Downloader Middleware。下面我們具體來看看 Downloader Middleware 的使用方法。

2.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。下面我們來看看這三個方法的詳細用法。

2.2.1 process_request(request, spider)

Request 被 Scrapy 引擎調度給 Downloader 之前,process_request() 方法就會被調用,也就是在 Request 從隊列里調度出來到 Downloader 下載執行之前,我們都可以用 process_request() 方法對 Request 進行處理。方法的返回值必須為 None、Response 對象、Request 對象之一,或者拋出 IgnoreRequest 異常。

process_request() 方法的參數有如下兩個。

  • request,即 Request 對象,即被處理的 Request;

  • spider,即 Spider 對象,即此 Request 對應的 Spider。

返回類型不同,產生的效果也不同。下面歸納一下不同的返回情況。

  • 當返回為 None 時,Scrapy 將繼續處理該 Request,接著執行其他 Downloader Middleware 的 process_request() 方法,直到 Downloader 把 Request 執行后得到 Response 才結束。這個過程其實就是修改 Request 的過程,不同的 Downloader Middleware 按照設置的優先級順序依次對 Request 進行修改,最后推送至 Downloader 執行。

  • 當返回為 Response 對象時,更低優先級的 Downloader Middleware 的 process_request() 和 process_exception() 方法就不會被繼續調用,每個 Downloader Middleware 的 process_response() 方法轉而被依次調用。調用完畢之后,直接將 Response 對象發送給 Spider 來處理。

  • 當返回為 Request 對象時,更低優先級的 Downloader Middleware 的 process_request() 方法會停止執行。這個 Request 會重新放到調度隊列里,其實它就是一個全新的 Request,等待被調度。如果被 Scheduler 調度了,那么所有的 Downloader Middleware 的 process_request() 方法會被重新按照順序執行。

  • 如果 IgnoreRequest 異常拋出,則所有的 Downloader Middleware 的 process_exception() 方法會依次執行。如果沒有一個方法處理這個異常,那么 Request 的 errorback() 方法就會回調。如果該異常還沒有被處理,那么它便會被忽略。

2.2.2 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() 方法順次處理。

  • 當返回為 Response 對象時,更低優先級的 Downloader Middleware 的 process_response() 方法會繼續調用,繼續對該 Response 對象進行處理。

  • 如果 IgnoreRequest 異常拋出,則 Request 的 errorback() 方法會回調。如果該異常還沒有被處理,那么它便會被忽略。

2.2.3 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() 方法轉而被依次調用。

  • 當返回為 Request 對象時,更低優先級的 Downloader Middleware 的 process_exception() 也不再被繼續調用,該 Request 對象會重新放到調度隊列里面等待被調度,它相當于一個全新的 Request。然后,該 Request 又會被 process_request() 方法順次處理。

以上內容便是這三個方法的詳細使用邏輯。在使用它們之前,請先對這三個方法的返回值的處理情況有一個清晰的認識。在自定義 Downloader Middleware 的時候,也一定要注意每個方法的返回類型。

下面我們用一個案例實戰來加深一下對 Downloader Middleware 用法的理解。

3.項目實戰

新建一個項目,命令如下所示:

scrapy startproject scrapydownloadertest

新建了一個 Scrapy 項目,名為 scrapydownloadertest。進入項目,新建一個 Spider,命令如下所示:

scrapy genspider httpbin httpbin.org

新建了一個 Spider,名為 httpbin,源代碼如下所示:

import scrapy class HttpbinSpider(scrapy.Spider):name = 'httpbin'allowed_domains = ['httpbin.org']start_urls = ['http://httpbin.org/']def parse(self, response):pass

接下來我們修改 start_urls 為:[‘http://httpbin.org/’]。隨后將 parse() 方法添加一行日志輸出,將 response 變量的 text 屬性輸出,這樣我們便可以看到 Scrapy 發送的 Request 信息了。
修改 Spider 內容如下所示:

import scrapyclass HttpbinSpider(scrapy.Spider):name = 'httpbin'allowed_domains = ['httpbin.org']start_urls = ['http://httpbin.org/get']def parse(self, response):self.logger.debug(response.text)

接下來運行此 Spider,執行如下命令:

scrapy crawl httpbin

Scrapy 運行結果包含 Scrapy 發送的 Request 信息,內容如下所示:

{"args": {}, "headers": {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Encoding": "gzip,deflate,br", "Accept-Language": "en", "Connection": "close", "Host": "httpbin.org", "User-Agent": "Scrapy/1.4.0 (+http://scrapy.org)"}, "origin": "60.207.237.85", "url": "http://httpbin.org/get" }

我們觀察一下 Headers,Scrapy 發送的 Request 使用的 User-Agent 是 Scrapy/1.4.0(+http://scrapy.org),這其實是由 Scrapy 內置的 UserAgentMiddleware 設置的,UserAgentMiddleware 的源碼如下所示:

from scrapy import signalsclass UserAgentMiddleware(object):def __init__(self, user_agent='Scrapy'):self.user_agent = user_agent@classmethoddef from_crawler(cls, crawler):o = cls(crawler.settings['USER_AGENT'])crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)return odef spider_opened(self, spider):self.user_agent = getattr(spider, 'user_agent', self.user_agent)def process_request(self, request, spider):if self.user_agent:request.headers.setdefault(b'User-Agent', self.user_agent)

在 from_crawler() 方法中,首先嘗試獲取 settings 里面的 USER_AGENT,然后把 USER_AGENT 傳遞給 init() 方法進行初始化,其參數就是 user_agent。如果沒有傳遞 USER_AGENT 參數就默認設置為 Scrapy 字符串。我們新建的項目沒有設置 USER_AGENT,所以這里的 user_agent 變量就是 Scrapy。接下來,在 process_request() 方法中,將 user-agent 變量設置為 headers 變量的一個屬性,這樣就成功設置了 User-Agent。因此,User-Agent 就是通過此 Downloader Middleware 的 process_request() 方法設置的。
修改請求時的 User-Agent 可以有兩種方式:一是修改 settings 里面的 USER_AGENT 變量;二是通過 Downloader Middleware 的 process_request() 方法來修改。

第一種方法非常簡單,我們只需要在 setting.py 里面加一行 USER_AGENT 的定義即可:

USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'

一般推薦使用此方法來設置。但是如果想設置得更靈活,比如設置隨機的 User-Agent,那就需要借助 Downloader Middleware 了。所以接下來我們用 Downloader Middleware 實現一個隨機 User-Agent 的設置。

在 middlewares.py 里面添加一個 RandomUserAgentMiddleware 的類,如下所示:

import randomclass RandomUserAgentMiddleware():def __init__(self):self.user_agents = ['Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)','Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2','Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1']def process_request(self, request, spider):request.headers['User-Agent'] = random.choice(self.user_agents)

我們首先在類的 init() 方法中定義了三個不同的 User-Agent,并用一個列表來表示。接下來實現了 process_request() 方法,它有一個參數 request,我們直接修改 request 的屬性即可。在這里我們直接設置了 request 對象的 headers 屬性的 User-Agent,設置內容是隨機選擇的 User-Agent,這樣一個 Downloader Middleware 就寫好了。

不過,要使之生效我們還需要再去調用這個 Downloader Middleware。在 settings.py 中,將 DOWNLOADER_MIDDLEWARES 取消注釋,并設置成如下內容:

DOWNLOADER_MIDDLEWARES = {'scrapydownloadertest.middlewares.RandomUserAgentMiddleware': 543,}

接下來我們重新運行 Spider,就可以看到 User-Agent 被成功修改為列表中所定義的隨機的一個 User-Agent 了:

{"args": {}, "headers": {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Encoding": "gzip,deflate,br", "Accept-Language": "en", "Connection": "close", "Host": "httpbin.org", "User-Agent": "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)"}, "origin": "60.207.237.85", "url": "http://httpbin.org/get" }

我們就通過實現 Downloader Middleware 并利用 process_request() 方法成功設置了隨機的 User-Agent。

另外,Downloader Middleware 還有 process_response() 方法。Downloader 對 Request 執行下載之后會得到 Response,隨后 Scrapy 引擎會將 Response 發送回 Spider 進行處理。但是在 Response 被發送給 Spider 之前,我們同樣可以使用 process_response() 方法對 Response 進行處理。比如這里修改一下 Response 的狀態碼,在 RandomUserAgentMiddleware 添加如下代碼:

def process_response(self, request, response, spider):response.status = 201return response

我們將 response 對象的 status 屬性修改為 201,隨后將 response 返回,這個被修改后的 Response 就會被發送到 Spider。

我們再在 Spider 里面輸出修改后的狀態碼,在 parse() 方法中添加如下的輸出語句:

self.logger.debug('Status Code: ' + str(response.status))

重新運行之后,控制臺輸出了如下內容:

[httpbin] DEBUG: Status Code: 201

可以發現,Response 的狀態碼成功修改了。因此要想對 Response 進行處理,就可以借助于 process_response() 方法。

另外還有一個 process_exception() 方法,它是用來處理異常的方法。如果需要異常處理的話,我們可以調用此方法。不過這個方法的使用頻率相對低一些,在此不用實例演示。

4.本節代碼

本節源代碼為:
https://github.com/Python3WebSpider/ScrapyDownloaderTest

5. 結語

本節講解了 Spider Middleware 和 Downloader Middleware 的基本用法。利用它們我們可以方便地實現爬蟲邏輯的靈活處理,需要好好掌握。

總結

以上是生活随笔為你收集整理的第44讲:scrapy中间键Middleware的使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。