目錄
- 1. 下載器中間件
- 1.1 核心方法
- 1.2 內建下載器中間件
- 1.3 案例:設定隨機請求頭
- 1.4 設定代理 IP
- 1.4.1 隨機代理 IP 中間件實作網路請求
- 1.5 設定 Cookies
- 1.5.1 案例:通過 Cookies 模擬自動登錄
- 2. 專案檔案目錄總結
1. 下載器中間件

Scrapy 允許使用中間件干預資料的抓取程序,以及完成其他資料處理作業,其中一類非常重要的中間件就是 下載器中間件,下載器中間件 可以對資料的下載和處理程序進行攔截,在 Scrapy 爬蟲中,資料下載和處理分為如下兩步完成,
- 指定
Web資源的URL,并向服務端發送請求,這一步需要依賴爬蟲類的start_urls變數或start_requests方法, - 當服務端回應
Scrapy爬蟲的請求后,就會回傳回應資料,這時系統會將回應資料再交由Scrapy爬蟲處理,也就是呼叫爬蟲類的請求回呼方法,如parse,
1.1 核心方法
下載中間件可以對上面兩步進行攔截,當爬蟲向服務端發送請求之前,會通過下載器中間件類的 process_request 方法進行攔截,當爬蟲處理服務端回應資料之前,會通過下載器中間件類的 process_response 方法進行攔截,除了這兩個方法之外,下載器中間件類還有一個 process_exception 方法,用于處理抓取資料程序中的例外,
這 3個 下載器中間件方法的完整描述如下:
(1) process_request(request, spider):

process_request 方法回傳值型別不同,產生的效果也不同,下面看一下不同型別回傳值的效果:
(1) None:Scrapy 會繼續處理該 Request,然后接著執行其他下載器中間件的 process_request 方法,直到下載器向服務端發送請求后,得到回應結果才結束,不同下載器中間件的 process_request 方法會按照優先級順序依次執行,當所有的下載器中間件的 process_request 都執行完,就會將 Request 送到下載器開始下載資料,
(2) Response 物件:優先級更低的下載器中間件的 process _request 方法不會再繼續呼叫,轉而開始呼叫每個下載器中間件的 process_response 方法,
(3) Request 物件:優先級更低的下載器中間件的 process_request 方法不會再繼續呼叫,并將這個 Request 物件放到調度佇列里,其實這就是一個全新的 Request 物件,等待被調度,如果這個 Request 被調度器調度了,那么所有的下載器中間件的 process_request 方法會重新按照順序執行,
(4) IgnoreRequest 例外:如果 IgnoreRequest 例外拋出,則所有的 Downloader Middleware 的 process_exception 方法會依次執行,如果沒有一個方法處理這個例外,那么 Request 的 errorback 方法就會回呼,
(2) process_response(request, response, spider):

process_response 方法回傳值型別不同,產生的效果也不同,下面看一下不同型別回傳值的效果:
(1) Request 物件:優先級更低的下載器中間件的 process_response 方法不會再繼續呼叫,該 Request 物件會重新放到調度佇列中等待被調度,相當于一個全新的 Request,然后,該 Request 會被 process_request 方法順序處理,
(2) Response 物件:更低優先級的下載器中間的 process_response 方法會繼續呼叫,繼續對該 Response 物件進行處理,
(3) IgnoreRequest 例外:Request 的 errorback 方法會回呼,如果該例外未被處理,會被忽略,
(3) process_exception(request, exception, spider):

process_exception 方法回傳值型別不同,產生的效果也不同,下面看一下不同型別回傳值的效果:
(1) None:更低優先級的下載器中間件的 process_exception 方法不再被繼續呼叫,每個下載器中間件,
(2) Response 物件:更低優先級的下載器中間件的 process_exception 方法不再被繼續呼叫,每個下載器中間件的 process_response 方法轉而被依次呼叫,
(3) Request 物件:更低優先級的下載器中間件的 process_exception 方法不再被繼續呼叫,該 Request 物件會重新放到調度佇列里面等待被調度,相當于一個全新的 Request,然后,該 Request 會被 process_request 方法按優先級順序處理,
1.2 內建下載器中間件
Scrapy 提供了很多內建的下載器中間件,例如下載超時、自動重定向、設定默認 HTTP 請求頭等,這些中間件都在 DOWNLOADER_MIDDLEWARES_BASE 變數中定義,該變數是字典型別,key 表示中間件的名字,value 表示中間件的優先等級,該變數的內容如下:


其他內置中間件寫法類似,優先等級是一個數字,數字小的中間件會被優先呼叫,所以 Scrapy 內建的下載器中間件中,RobotsTxtMiddleware 中間件會被第一個呼叫,因為該中間件的優先級是100,如果要禁止某個內建的中間件,需要將優先級設定為 None,代碼如下:
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None
1.3 案例:設定隨機請求頭
設定請求頭是爬蟲程式中必不可少的一項設定,大多數網站都會根據請求頭內容指定一些反爬策略,在 Scrapy 框架中如果只是簡單地設定一個請求頭的話,可以在當前的爬蟲檔案中以引數的形式添加在網路請求當中,
(1) 使用下面的命令創建一個新的 Scrapy 工程以及爬蟲檔案,
scrapy startproject middlePro
cd middlePro
scrapy genspider DownloaderSpider www.xxx.com
工程的目錄結構如下圖所示:

示例代碼如下:
import scrapy # 匯入框架
class DownloaderspiderSpider(scrapy.Spider):
name = 'DownloaderSpider' # 定義爬蟲名稱
# allowed_domains = ['www.xxx.com']
def start_requests(self):
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/87.0.4280.88 Safari/537.36'
}
return [scrapy.Request('http://httpbin.org/get', headers=self.headers, callback=self.parse)]
def parse(self, response):
print(response.text) # 列印回傳的回應資訊
在 settings.py 中做如下的設定:

程式運行結果如圖所示:

在沒有使用指定的請求頭時,發送網路請求將使用 Scrapy 默認的請求頭資訊,資訊內容如下:

對于實作多個網路請求時,最好每發送一次請求就更換一個請求頭,這樣就可以避免請求頭的反爬策略,對于這樣的需求可以使用自定義中間件的方式實作一個設定隨機請求頭的中間件,具體實作步驟如下:
(1) 打開命令視窗,進入到虛擬環境(根據讀者實際情況決定),首先通過 scrapy startproject header 創建一個名稱為 header 的專案,然后通過 cd header 命令打開專案最外層的檔案夾,最后通過 scrapy genspider HeaderSpider quotes.toscrape.com 命令創建名稱為 HeaderSpider 的爬蟲檔案,命令操作如下圖所示:

(2) 打開 HeaderSpider 檔案,配置測驗網路請求的爬蟲代碼,代碼如下:
import scrapy
class HeaderspiderSpider(scrapy.Spider):
name = 'HeaderSpider'
allowed_domains = ['quotes.toscrape.com']
# start_urls = ['http://quotes.toscrape.com/']
def start_requests(self):
# 設定爬取目標的地址
urls = ['http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/']
# 獲取所有地址,有幾個地址則發送幾次請求
for url in urls:
# 發送網路請求
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
# 列印每次網路請求的請求頭資訊
print(f"請求頭資訊為: {response.request.headers.get('User-Agent')}")
(3) 安裝 fake-useragent 模塊,打開 middlewares.py 檔案,在該檔案中首先匯入 fake-useragent 模塊中的 User-Agent 類,然后創建 RandomHeaderMiddleware 類并通過 init() 函式進行類的初始化作業,代碼如下:
from fake_useragent import UserAgent # 匯入請求頭類
# 自定義隨機請求頭的中間件
class RandomHeaderMiddleware(object):
def __init__(self, crawler):
self.ua = UserAgent() # 隨機請求頭物件
# 如果組態檔中不存在就使用默認的 Google Chrome請求頭
self.type = crawler.settings.get('RANDOM_UA_TYPE', 'chrome')
(4) 重寫 from_crawler() 方法,在該方法中將 cls() 實體物件回傳,代碼如下:
@classmethod
def from_crawler(cls, crawler):
return cls(crawler) # 回傳 cls() 實體物件
(5) 重寫 process_request() 方法,在該方法中實作設定隨機生成的請求頭資訊,代碼如下:
# 發送網路請求時呼叫該方法
def process_request(self, request, spider):
# 設定隨機生成的請求頭 getattr()函式:獲取物件屬性的值
request.headers.setdefault('User-Agent', getattr(self.ua, self.type))
(6) 打開 settings.py 檔案,在該檔案中找到 DOWNLOADER_MIDDLEWARES 配置資訊,然后配置自定義的請求頭中間件,并把默認生成的下載中間件禁用,最后在配置資訊的下面添加請求頭型別,代碼如下:
ROBOTSTXT_OBEY = False
DOWNLOADER_MIDDLEWARES = {
# 'header.middlewares.HeaderDownloaderMiddleware': 543,
# 啟動自定義隨機請求頭中間件
'header.middlewares.RandomHeaderMiddleware': 400,
# 設為None,禁用默認創建的下載中間件
'header.middlewares.HeaderDownloaderMiddleware': None
}
# 配置請求頭型別為隨機,此處還設定為ie,firefox以及chrome
RANDOM_UA_TYPE = "random"
LOG_LEVEL = "ERROR"
(7) 啟動 HeaderSpider 爬蟲,控制臺輸出兩次請求,并分別使用不同的請求資訊,如下圖所示:

1.4 設定代理 IP
使用代理 IP 實作網路爬蟲是有效解決反爬蟲的一種方法,如果只是想在 Scrapy 中簡單地應用一次代理 IP 時可以使用以下代碼:
import scrapy
class HeaderspiderSpider(scrapy.Spider):
name = 'HeaderSpider'
def start_requests(self):
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/87.0.4280.88 Safari/537.36'
}
return [
scrapy.Request('http://httpbin.org/get', callback=self.parse,
meta={'proxy': 'http://123.73.63.178:32223'}, headers=self.headers)]
# 回應資訊
def parse(self, response):
print(response.text) # 列印回傳的回應資訊
程式運行結果如下圖所示:

在使用代理 IP 發送網路請求時,需要確保代理 IP 是一個有效的 IP,否則會出現錯誤,關于 IP 的獲取,可以查看博主的 Python每日一練(24)-requests 模塊獲取免費的代理并檢測代理 IP 是否有效 一文,
1.4.1 隨機代理 IP 中間件實作網路請求
如果需要發送多個網路請求時,可以自定義一個代理 IP 的中間件,在這個中間件中使用隨機的方式從代理 IP 串列內隨機抽取一個有效的代理 IP,并通過這個有效的代理 IP 實作網路請求,實作的具體步驟如下:
(1) 在 InSpider.py 檔案中撰寫爬蟲代碼,代碼如下:

(2) 打開 middlewares.py 檔案,在該檔案中創建 IpdemoProxyMiddleware 類,然后定義保存代理 IP 的串列,最后重寫 process_request() 方法在該方法中實作發送網路請求時隨機抽取有效的代理 IP,代碼如下:
import random
class IpdemoProxyMiddleware(object):
# 定義有效的代理IP串列
PROXIES = [
'223.156.165.56:18541',
'183.150.159.146:28803',
'182.38.125.139:42642',
'123.73.209.86:32223',
'123.73.209.34:32223'
]
# 發送網路請求時呼叫
def process_request(self, request, spider):
proxy = random.choice(self.PROXIES) # 隨機抽取代理IP
request.meta['proxy'] = 'http://' + proxy # 設定網路請求所用的代理IP
(3) 在 settings.py 檔案中修改 DOWNLOADER_MIDDLEWARES 配置資訊,激活自定義隨機獲取代理 IP 的中間件,如下:

執行兩次爬蟲檔案,程式運行結果如下圖所示:


由于上面示例中的代理 IP 均為免費的代理 IP,所以讀者在運行示例代碼時需要將其替換為最新可用的代理 IP,
1.5 設定 Cookies
熟練使用 Cookies 在撰寫爬蟲程式時是非常重要的,Cookies 代表著用戶資訊,如果需要爬取登錄后網頁的資訊時,就可以將 Cookies 資訊保存,然后在第二次獲取登錄后的資訊時就不需要再次登錄了,直接使用 Cookies 進行登錄即可,在 Scrapy 中,如果想在 Spider(爬蟲) 檔案中直接定義并設定 Cookies 引數時,可以參考以下示例代碼:
import scrapy
class CookiespiderSpider(scrapy.Spider):
name = 'CookieSpider'
allowed_domains = ['httpbin.org/get'] # 域名串列
start_urls = ['http://httpbin.org/get']
cookies = {"CookiesDemo": "python"} # 模擬Cookies資訊
def start_requests(self):
# 發送網路請求,請求地址為 start_urls 串列中的第一個地址
yield scrapy.Request(url=self.start_urls[0], cookies=self.cookies,
callback=self.parse)
# 回應資訊
def parse(self, response):
# 列印結果
print(response.text)
pass
程式運行結果如下圖所示:

以上示例代碼中的 Cookies 是一個模擬測驗所使用的資訊,并不是一個真實有效的 Cookies 資訊,所以在使用時需要將 Cookies 資訊設定為爬取網站對應的真實 Cookies,
1.5.1 案例:通過 Cookies 模擬自動登錄
在 Scrapy 中除了使用以上示例代碼中的方法設定 Cookies 意外,也可以使用自定義中間件的方式設定 Cookies,以爬取某網站登錄后的用戶名資訊為例,具體實作步驟如下:
(1) 在 CookieSpider.py 檔案中撰寫爬蟲代碼,代碼如下:
import scrapy
class CookiespiderSpider(scrapy.Spider):
name = 'CookieSpider'
allowed_domains = ['douban.com'] # 域名串列
start_urls = ['https://www.douban.com/'] # 請求初始化串列
# cookies = {"CookiesDemo": "python"} # 模擬Cookies資訊
def start_requests(self):
# 發送網路請求,請求地址為 start_urls 串列中的第一個地址
yield scrapy.Request(url=self.start_urls[0], callback=self.parse)
# 回應資訊
def parse(self, response):
# 列印登錄后的用戶資訊名
print(response.xpath('//*[@id="db-global-nav"]/div/div[1]/ul/li[2]/a/span[1]/text()').extract_first())
pass
(2) 在 middlewares.py 檔案中,定義用于格式化與設定 Cookies 的中間件,代碼如下:
# 自定義Cookies中間件
class CookiesdemoMiddleware(object):
# 初始化
def __init__(self, cookies_str):
self.cookies_str = cookies_str
@classmethod
def from_crawler(cls, crawler):
return cls(
# 獲取組態檔中的Cookies資訊
cookies_str=crawler.settings.get('COOKIES_DEMO')
)
cookies = {}
def process_request(self, request, spider):
for cookie in self.cookies_str.split(";"): # 通過;分割Cookies字串
key, value = cookie.split("=", 1) # 將key與值進行分割
self.cookies.__setitem__(key, value) # 將分割后的資料保存至字典中
request.cookies = self.cookies # 設定格式化以后的Cookies
(4) 在 middlewares.py 檔案中,定義隨機設定請求頭的中間件,代碼如下:
from fake_useragent import UserAgent # 匯入請求頭類
# 自定義隨機請求頭的中間件
class RandomHeaderMiddleware(object):
def __init__(self, crawler):
self.ua = UserAgent() # 隨機請求頭物件
# 如果組態檔中不存在就使用默認的 Google Chrome請求頭
self.type = crawler.settings.get('RANDOM_UA_TYPE', 'chrome')
@classmethod
def from_crawler(cls, crawler):
return cls(crawler) # 回傳 cls() 實體物件
# 發送網路請求時呼叫該方法
def process_request(self, request, spider):
# 設定隨機生成的請求頭 getattr()函式:獲取物件屬性的值
request.headers.setdefault('User-Agent', getattr(self.ua, self.type))
(4) 打開 settings.py 檔案,在該檔案中首先將 DOWNLOADER_MIDDLEWARES 配置資訊中的默認配置資訊禁用,然后添加用于處理 Cookies 與隨機請求頭的配置資訊并激活,最后定義從瀏覽器中獲取的 Cookies 資訊,代碼如下:

程式運行結果如下:

2. 專案檔案目錄總結
目錄結構中的檔案說明如下:
- spiders(檔案夾):用于創建爬蟲檔案,撰寫爬蟲規則,
- __init__.py 檔案:初始化檔案,
- items.py 檔案:用于資料的定義,可以寄存處理后的資料,
- middlewares.py:定義爬取時的中間件,其中包括 SpiderMiddleware(爬蟲中間件)、DownloaderMiddleware(下載中間件),
- pipelines.py 檔案:用于實作清洗資料、驗證資料、保存資料,
- settings.py 檔案:整個框架的組態檔,主要包含配置爬蟲資訊,請求頭、中間件等,
- scrapy.cfg 檔案:專案部署檔案,其中定義了專案的組態檔路徑等相關資訊,
scrapy.Spider 類中的常用屬性與方法含義如下:
- name:用于定義一個爬蟲名稱的字串,Scrapy 通過這個爬蟲名稱進行爬蟲的查找,所以這個名稱必須是唯一的,不過我們可以生成多個相同的爬蟲實體,如果爬取單個網站一般會用這個網站的名稱作為爬蟲的名稱,
- allowed_domains:包含了爬蟲允許爬取的域名串列,當OffsiteMiddleware 啟用時,域名不在串列中的 URL 不會被爬取,
- start_urls:URL 的初始串列,如果沒有指定特定的 URL,爬蟲將從該串列中進行爬取,
- custom_settings:這是一個專屬于當前爬蟲的配置,是一個字典型別的資料,設定該屬性會覆寫整個專案的全域,所以在設定該屬性時必須在實體化前更新,必須定義為類變數,
- settings:這是一個 settings 物件,通過它,我們可以獲取專案的全域設定變數,
- logger:使用 Spider 創建的 Python 日志器,
- start_requests():該方法用于生成網路請求,它必須回傳一個可迭代物件,該方法默認使用 start_urls 中的 URL 來生成request,而 request 的請求方式為 GET,如果我們想通過 POST 方式請求網頁時,可以使用 FormRequest() 重寫該方法,
- parse():如果 response 沒有指定回呼函式時,該方法是 Scrapy 處理 response 的默認方法,該方法負責處理 response 并回傳處理的資料和下一步請求,然后回傳一個包含 request 或 Item 的可迭代物件,
- closed():當爬蟲關閉時,該函式會被呼叫,該方法用于代替監聽作業,可以定義釋放資源或是收尾操作,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/236598.html
標籤:python
