爬蟲之scrapy中間件的使用
1. scrapy中間件的分類和作用
1.1 scrapy中間件的分類
根據scrapy運行流程中所在位置不同分為:【中間件離哪個模塊近就稱為什么中間件】
- 下載中間件
- 爬蟲中間件
1.2 scrapy中間的作用:預處理request和response物件
- 對header以及cookie進行更換和處理
- 使用代理ip等
- 對請求進行定制化操作,
但在scrapy默認的情況下 兩種中間件都在middlewares.py一個檔案中 【平時主要用下載中間件】
爬蟲中間件使用方法和下載中間件相同,且功能重復,通常使用下載中間件
2. 下載中間件的使用方法:
通過下載中間件來學習如何使用中間件 撰寫一個Downloader Middlewares和我們撰寫一個pipeline一樣,定義一個類,然后在setting中開啟
Downloader Middlewares默認的方法:
-
process_request(self, request, spider):【此方法是用的最多的】
- 當每個request通過下載中間件時,該方法被呼叫,
- 回傳None值:沒有return也是回傳None,該request物件傳遞給下載器,或通過引擎傳遞給其他權重低的process_request方法 【如果所有的下載器中間件都回傳為None,則請求最終被交給下載器處理】
- 回傳Response物件:不再請求,把response回傳給引擎 【如果回傳為請求,則將請求交給調度器】
- 回傳Request物件:把request物件通過引擎交給調度器,此時將不通過其他權重低的process_request方法 【將回應物件交給spider進行決議】
-
process_response(self, request, response, spider):
- 當下載器完成http請求,傳遞回應給引擎的時候呼叫
- 回傳Resposne:通過引擎交給爬蟲處理或交給權重更低的其他下載中間件的process_response方法 【如果回傳為請求,則將請求交給調度器】
- 回傳Request物件:通過引擎交給調取器繼續請求,此時將不通過其他權重低的process_request方法 【將回應物件交給spider進行決議】
-
在settings.py中配置開啟中間件,權重值越小越優先執行 【同管道的注冊使用】
運行爬蟲觀察現象




此時示例代碼:
import scrapy
class MovieSpider(scrapy.Spider):
name = 'movie'
allowed_domains = ['douban.com']
start_urls = ['https://movie.douban.com/top250']
def parse(self, response):
li = response.xpath('//*[@class="info"]')
print(len(li))
運行效果:

解決這個錯誤:


再次運行效果:

修改爬蟲代碼
示例代碼:
import scrapy
from douban.items import DoubanItem
class MovieSpider(scrapy.Spider):
name = 'movie'
allowed_domains = ['douban.com']
start_urls = ['https://movie.douban.com/top250']
def parse(self, response):
el_list = response.xpath('//*[@class="info"]')
print(len(el_list))
for el in el_list:
item = DoubanItem()
item['name'] = el.xpath('./div[1]/a/span[1]/text()').extract_first()
item['score'] = el.xpath('./div[2]/div/span[2]/text()').extract_first()
# ......
print(item)
url = response.xpath('//span[@class="next"]/a/@href').extract_first()
if url != None:
url = response.urljoin(url)
yield scrapy.Request(
url=url
)
運行效果:

3. 定義實作隨機User-Agent的下載中間件
3.1 在middlewares.py中完善代碼
import random
from Tencent.settings import USER_AGENTS_LIST # 注意匯入路徑,請忽視pycharm的錯誤提示
class UserAgentMiddleware(object):
def process_request(self, request, spider):
user_agent = random.choice(USER_AGENTS_LIST)
request.headers['User-Agent'] = user_agent
# 不寫return
class CheckUA:
def process_response(self,request,response,spider):
print(request.headers['User-Agent'])
return response # 不能少!
3.2 在settings中設定開啟自定義的下載中間件,設定方法同管道
DOWNLOADER_MIDDLEWARES = {
'Tencent.middlewares.UserAgentMiddleware': 543, # 543是權重值
'Tencent.middlewares.CheckUA': 600, # 先執行543權重的中間件,再執行600的中間件
}
3.3 在settings中添加UA的串列
USER_AGENTS_LIST = [
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
"Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
"Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
"Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
"Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5"
]
詳細思路分析:

寫在setting中:



示例代碼:
from scrapy import signals
import random
from douban.settings import USER_AGENTS_LIST
# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter
# 定義一個中間件類
class RandomUserAgent(object):
def process_request(self, request, spider):
# print(request.headers['User-Agent'])
pass

運行效果:

修改完善代碼:

示例代碼:
from scrapy import signals
import random
from douban.settings import USER_AGENTS_LIST
# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter
# 定義一個中間件類
class RandomUserAgent(object):
def process_request(self, request, spider):
# print(request.headers['User-Agent'])
ua = random.choice(USER_AGENTS_LIST)
request.headers['User-Agent'] = ua
運行效果:

4. 代理ip的使用
4.1 思路分析
- 代理添加的位置:request.meta中增加
proxy欄位 - 獲取一個代理ip,賦值給
request.meta['proxy']- 代理池中隨機選擇代理ip
- 代理ip的webapi發送請求獲取一個代理ip
4.2 具體實作
免費代理ip:
class ProxyMiddleware(object):
def process_request(self,request,spider):
# proxies可以在settings.py中,也可以來源于代理ip的webapi
# proxy = random.choice(proxies)
# 免費的會失效,報 111 connection refused 資訊!重找一個代理ip再試
proxy = 'https://1.71.188.37:3128'
request.meta['proxy'] = proxy
return None # 可以不寫return
收費代理ip:
# 人民幣玩家的代碼(使用abuyun提供的代理ip)
import base64
# 代理隧道驗證資訊 這個是在那個網站上申請的
proxyServer = 'http://proxy.abuyun.com:9010' # 收費的代理ip服務器地址,這里是abuyun
proxyUser = 用戶名
proxyPass = 密碼
proxyAuth = "Basic " + base64.b64encode(proxyUser + ":" + proxyPass)
class ProxyMiddleware(object):
def process_request(self, request, spider):
# 設定代理
request.meta["proxy"] = proxyServer
# 設定認證
request.headers["Proxy-Authorization"] = proxyAuth
4.3 檢測代理ip是否可用
在使用了代理ip的情況下可以在下載中間件的process_response()方法中處理代理ip的使用情況,如果該代理ip不能使用可以替換其他代理ip
class ProxyMiddleware(object):
......
def process_response(self, request, response, spider):
if response.status != '200':
request.dont_filter = True # 重新發送的請求物件能夠再次進入佇列
return requst
在settings.py中開啟該中間件


示例代碼:
from scrapy import signals
import random
from douban.settings import USER_AGENTS_LIST, PROXY_LIST
import base64
class RandomProxy(object):
def process_request(self, request, spider):
proxy = random.choice(PROXY_LIST)
print(proxy)
if 'user_passwd' in proxy:
# 對賬號密碼進行編碼
b64_up = base64.b64encode(proxy['user_passwd']).encode()
# 設定認證
request.headers['Proxy-Authorization'] = 'Basic ' + base64.decode()
# 設定代理
request.meta['proxy'] = proxy['ip_port']
else:
request.meta['proxy'] = proxy['ip_port']

5. 在中間件中使用selenium
以github登陸為例
5.1 完成爬蟲代碼
import scrapy
class Login4Spider(scrapy.Spider):
name = 'login4'
allowed_domains = ['github.com']
start_urls = ['https://github.com/1596930226'] # 直接對驗證的url發送請求
def parse(self, response):
with open('check.html', 'w') as f:
f.write(response.body.decode())
5.2 在middlewares.py中使用selenium
import time
from selenium import webdriver
def getCookies():
# 使用selenium模擬登陸,獲取并回傳cookie
username = input('輸入github賬號:')
password = input('輸入github密碼:')
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
driver = webdriver.Chrome('/home/worker/Desktop/driver/chromedriver',
chrome_options=options)
driver.get('https://github.com/login')
time.sleep(1)
driver.find_element_by_xpath('//*[@id="login_field"]').send_keys(username)
time.sleep(1)
driver.find_element_by_xpath('//*[@id="password"]').send_keys(password)
time.sleep(1)
driver.find_element_by_xpath('//*[@id="login"]/form/div[3]/input[3]').click()
time.sleep(2)
cookies_dict = {cookie['name']: cookie['value'] for cookie in driver.get_cookies()}
driver.quit()
return cookies_dict
class LoginDownloaderMiddleware(object):
def process_request(self, request, spider):
cookies_dict = getCookies()
print(cookies_dict)
request.cookies = cookies_dict # 對請求物件的cookies屬性進行替換
組態檔中設定開啟該中間件后,運行爬蟲可以在日志資訊中看到selenium相關內容

示例代碼:
import time
from selenium import webdriver
from scrapy.http import HtmlResponse
class SeleniumMiddleware(object):
def process_request(self, request, spider):
url = request.url
if 'daydata' in url:
driver = webdriver.Chrome()
driver.get(url)
time.sleep(3)
data = driver.page_source
driver.close()
# 創建回應物件
res = HtmlResponse(url=url, body=data, encoding='utf-8', request=request)
return res

小結
中間件的使用:
-
完善中間件代碼:
-
process_request(self, request, spider):
- 當每個request通過下載中間件時,該方法被呼叫,
- 回傳None值:沒有return也是回傳None,該request物件傳遞給下載器,或通過引擎傳遞給其他權重低的process_request方法
- 回傳Response物件:不再請求,把response回傳給引擎
- 回傳Request物件:把request物件通過引擎交給調度器,此時將不通過其他權重低的process_request方法
-
process_response(self, request, response, spider):
- 當下載器完成http請求,傳遞回應給引擎的時候呼叫
- 回傳Resposne:通過引擎交給爬蟲處理或交給權重更低的其他下載中間件的process_response方法
- 回傳Request物件:通過引擎交給調取器繼續請求,此時將不通過其他權重低的process_request方法
-
-
需要在settings.py中開啟中間件 DOWNLOADER_MIDDLEWARES = { 'myspider.middlewares.UserAgentMiddleware': 543, }
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/260059.html
標籤:python
上一篇:python應用學習(五)——requests爬取網頁圖片
下一篇:matplotlib必備圖表繪制
