爬蟲中間件
- 特點:主要處理蜘蛛(
Spider
)和下載器(Downloader
)之間的請求和響應。可以對蜘蛛生成的請求進行攔截、修改或過濾,也可以對下載器返回給蜘蛛的響應進行處理。 - 適用場景:
- 請求過濾與修改:當需要根據蜘蛛的某些條件對生成的請求進行過濾或修改時,例如根據蜘蛛的狀態、爬取深度等決定是否發送某個請求,或者修改請求的參數、URL 等。
- 響應處理:對下載器返回的響應進行統一的預處理,比如檢查響應的狀態碼,根據不同的狀態碼進行不同的處理;或者對響應內容進行初步的清洗、解析,提取一些公共信息供蜘蛛后續使用。
- 實現分布式爬蟲:在分布式爬蟲場景中,用于協調不同節點之間的爬取任務,例如根據節點的負載情況分配請求,或者對分布式環境下的請求和響應進行特殊處理,確保爬取任務的高效執行。
下載中間件
- 特點:主要作用于下載器層面,用于處理
HTTP
請求和響應。可以在請求發送到服務器之前對請求進行修改,如添加請求頭、代理設置等;也可以在響應返回后對響應進行處理,如解壓、解碼等操作。 - 適用場景:
- 請求頭設置:像前面提到的隨機設置
User-Agent
,以及添加其他自定義的請求頭信息,以偽裝請求來源或滿足網站的特定要求。 - 代理設置:當需要使用代理服務器來發送請求時,下載中間件是設置代理的合適位置。可以根據不同的條件動態選擇代理服務器,或者對代理的使用進行管理和監控,如處理代理的認證、檢測代理的可用性等。
- 響應處理:對響應進行一些與數據傳輸和格式相關的處理,例如對壓縮的響應進行解壓(如處理gzip壓縮的響應),對編碼后的響應進行解碼,確保蜘蛛接收到的是正確格式的響應內容。
- 請求頭設置:像前面提到的隨機設置
在實際的爬蟲項目中,通常會同時使用爬蟲中間件和下載中間件。下載中間件用于處理與 HTTP
請求和響應相關的底層操作,而爬蟲中間件則更側重于處理與蜘蛛邏輯相關的請求和響應,兩者結合可以滿足復雜的爬蟲需求。例如,在一個爬取電商網站的項目中,可能會使用下載中間件來設置代理和隨機User-Agent
,以避免被網站封禁;同時使用爬蟲中間件來根據商品的分類過濾請求,只爬取特定類別的商品信息,并對響應中的通用信息進行提取和處理。
DOWNLOADER_MIDDLEWARES = {# 其他中間件...'your_project_name.middlewares.ProxyMiddleware': 543,
}
543
:這是中間件的優先級,數值越小,中間件越先被執行。
代理中間件
import random
import base64class ProxyMiddleware:def __init__(self):self.proxy_list = ['http://proxy1.example.com:8080','http://proxy2.example.com:8080',]self.username = 'your_username'self.password = 'your_password'def process_request(self, request, spider):proxy = random.choice(self.proxy_list)request.meta['proxy'] = proxy# 添加代理認證信息if self.username and self.password:auth = f'{self.username}:{self.password}'auth_encoded = base64.b64encode(auth.encode('utf-8')).decode('utf-8')request.headers['Proxy-Authorization'] = f'Basic {auth_encoded}'return Nonedef process_exception(self, request, exception, spider):# 處理代理失敗的情況proxy = request.meta.get('proxy')if proxy in self.proxy_list:self.proxy_list.remove(proxy)return request
在 Scrapy
的下載器中間件中,process_request
方法的返回值有特定的含義:
return None
:這是最常見的返回值。當返回None
時,Scrapy 會繼續處理這個請求,也就是會將請求發送給下一個中間件(如果有的話),最終由下載器去執行請求操作。return Response
:若返回一個Response
對象,Scrapy 會停止處理這個請求的后續中間件,并且直接將這個Response
對象作為請求的響應返回給蜘蛛(Spider)進行處理。return Request
:要是返回一個新的Request
對象,Scrapy 會停止處理當前請求,然后將新的Request對象重新加入到請求隊列中,等待后續處理。
UserAgent中間件
class UserAgentMiddleware:def process_request(self, request, spider):user_agant = random.choice(USER_AGENT_LIST)# 添加請求頭request.headers['User-Agent'] = user_agantreturn None
Selenium中間件
from DrissionPage import Chromiumclass SeleniumMiddleware:def __init__(self, crawler):self.crawler = crawler# 連接 spider_closed 信號和 spider_closed 方法self.crawler.signals.connect(self.spider_closed, signal=signals.spider_closed)self.browser = Chromium()self.tab = self.browser.latest_tab@classmethoddef from_crawler(cls, crawler):return cls(crawler)def process_request(self, request, spider):self.tab.get(request.url)body = self.tab.htmlreturn HtmlResponse(url=request.url, body=body, request=request, encoding='utf-8')def spider_closed(self, spider):self.tab.close()self.browser.quit()