1.項目環境
基於 Python3.7 ,使用 Scrapy框架,正常抓取某網站搜索指數排名。
2.需求背景
由於網絡時好時壞,有時候會遇到timeout這種延時錯誤,出錯之後爬蟲便會停止,如果沒有第一時間重啓,或者是排錯,抓取效率便會十分低下。所以便想着有沒有解決方法(爬蟲項目是同事寫的,跑在我電腦上,實習生打雜就完事了
3.解決思路
-
1.利用另外一個Python程序,監控該爬蟲進程是否正常進行,同理也可以擴展到監控其他的程序是否正常運行。
-
2.利用Scrapy框架自身提供的中間件進行錯誤處理
3.1 進程監控
實現進程監控需要解決以下問題
- 如何用python獲取電腦中運行的進程?
- 如何知道要監控的進程是否存活?
第一個問題使用python提供的os庫可以解決
os.popen('tasklist')
tasklist : 輸出電腦上當前所執行的程序名已經進程編號PID
該方法類似與在cmd命令行下所執行的效果一樣,會將在cmd下執行的結果輸出到緩衝流,因此我們也需要從緩衝流中把結果讀出來。
os.popen('tasklist').read()
第二個問題,我們可以使用新建子進程來解決這個問題
from multiprocessing import Process
from scrapy import cmdline
def execute_spider()
cmdline.execute('scrapy crawl my_spider'.split())
my_pro = Process(target=executeSpider)
my_pro.start()
print("pid:%s =" % my_pro.getpid())
# 在這裏可以獲得pid,可以使用進程通信方式把pid傳遞過去,我比較簡單,使用的文件存儲pid,監控程序讀文件就好了
在這裏使用getpid()
便可以獲得該爬蟲的進程號,結合之前的命令,便可以知道當前程序是否正常運行。
# 從tasklist的輸出中,統計該pid的數量,從而判定該進程是否存在
isAlive = os.popen('tasklist').read().count(pid) != 0
3.2 Scrapy中間件之異常處理
在Scrapy中存在兩種中間件,一種是SpiderMiddleWare(蜘蛛中間件),一種是DownloaderMiddlerWare(下載器中間件)。
- 蜘蛛中間件: Scrapy中Engine組件與Spider交互經過的中間件,如果蜘蛛發生的異常,就在這個裏面處理,比如在解析數據的時候發生的錯誤。對應的方法如下:
"""
response: 異常被拋出時被處理的response對象
exception: 拋出的異常
spider: 拋出該異常的spider對象
"""
def process_spider_exception(self, response, exception, spider):
# 相應處理
pass
- 下載器中間件:Scrapy中Engine組件與Downloader交互經過的中間件,這個處理的錯誤一般都是網絡問題,或者服務器問題。與自己敲的蜘蛛代碼關係不大。(我選的這個
"""
request: 產生異常的request對象
exception: 拋出的異常對象
spider: 產生異常的request對象的spider對象
"""
def process_exception(self, request, exception, spider):
# 相應處理
pass
如果使用中間件,別忘記在setting.py文件中開啓中間件
DOWNLOADER_MIDDLEWARES = {
'MySpider.middlewares.MyDownloaderMiddleware': 543,
}
or 蜘蛛中間件
SPIDER_MIDDLEWARES = {
'MySpider.middlewares.MySpiderMiddleware': 543,
}
4.相應處理
現在已經知道進程是否正常、爬蟲出錯在哪處理。那麼如何處理呢?
4.1 繼續運行
第一種繼續運行的方式,可以使用死循環,也可以使用調度任務。
死循環實現比較簡單,在同一個代碼中,開啓一個進程執行Scrapy爬蟲,一個進程實現監控。(不推薦)
#偷懶使用死循環的方式
while True:
p = os.popen('tasklist')
pid = getPid() # 寫個讀文件的方法
isAlive = p.read().count(pid) != 0
count = count +1
print( "%s %s 次是 %s" %(datetime.datetime.now(),count,isAlive) )
if(isAlive==False):
my_pro = Process(target=executeSpider)
my_pro.start()
with open("pid.txt","w") as f:
f.write(my_pro.getpid())
time.sleep(60)
第二種利用中間件返回Request讓爬蟲繼續執行:
def process_exception(self, request, exception, spider):
with open("exceptionLog.txt", "a") as f:
msg = "%s : %s\n" %(datetime.datetime.now(),exception)
f.write(msg) #手動記錄日誌
return request
4.2 通知
第一種使用窗口彈出,適合在電腦工作時跑的爬蟲,這個窗口提醒會置頂,達到提醒的目的,同時程序也會阻塞,需要確認之後纔會繼續,當然如果有異常的話,也不需要繼續了(不適合打遊戲時開
需要安裝win32庫: pip install pywin32
win32api.MessageBox(0, "爬蟲進程異常,請查看", "提醒", win32con.MB_SYSTEMMODAL)
第二種使用郵件通知,適合不在電腦目前時跑的爬蟲。
(這個參考下一篇博文,封裝一個發郵件的類,調用一下便ok。
5. 總結
使用一個工具需要慢慢的熟悉,第一個監控進程的方法是在沒有弄清有兩個中間件導致一直沒找到解決方法所想的(囫圇吞棗學了一下Scapy。使用彈窗提醒還可以,同時也可以使用在其他可以在命令行所運行的程序上,見參考博客
借鑑博客如下
https://www.jb51.net/article/163273.htm
https://blog.csdn.net/weixin_41990342/article/details/81907263