爬蟲這型別程式典型特徵是意外多,無法確保每次請求都是穩定的返回統一的結果,要提高健壯性,能對錯誤資料or超時or程式死鎖等都能進行處理,才能確保程式幾個月不停止。本專案乃長期維護github:反反爬蟲開源庫中積累下來,更多幹貨歡迎star。
目錄:
- 一:基礎try&except異常處理
- 二:普通請求函式的超時處理
- 三:selenium+chrome | phantomjs 的超時處理
- 四:自定義函式的死鎖or超時處理
- 五:自定義執行緒的死鎖or超時處理
- 六:自重啟的程式設計
一:基礎try&except異常處理
try&except的語句作用不僅僅是要讓其捕獲異常更重要的是讓其忽略異常,因為爬蟲中的絕大多數異常可能重新請求就不存在,因此,發現異常的時候將其任務佇列進行修復其實是個最省力的好辦法。
其次被try包住的語句即使出錯也不會導致整個程式的退出,相信我,你絕對不希望計劃跑一個週末的程式在半夜停止了。
1 2 3 4 5 6 7 8 9 10 |
try: passhttp://top.jobbole.com/deliver-article/# #可能出錯的語句 except Exception,e: pass #保留錯誤的url,留待下次重跑 print e finally: #無論是否處理了異常都繼續執行 print time.ctime() |
二:請求函式的超時處理
2.1:普通請求:
2.1.1單請求型別:
1 2 |
import requests requests.get(url,timeout=60) |
1 2 3 |
import requesocks session = requesocks.session() response = session.get(URL,headers=headers,timeout=10) |
三:selenium+chrome | phantomjs 的超時處理
2.2.1:selenium+chrome的超時設定
官網原文:http://selenium-python.readthedocs.io/waits.html
顯式等待:、等待某個條件發生,然後再繼續進行程式碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Firefox() driver.get("http://somedomain/url_that_delays_loading") try: element = WebDriverWait(driver, 10).until( #這裡修改時間 EC.presence_of_element_located((By.ID, "myDynamicElement")) ) finally: driver.quit() |
隱式等待:是告訴WebDriver在嘗試查詢一個或多個元素(如果它們不是立即可用的)時輪詢DOM一定時間。預設設定為0,一旦設定,將為WebDriver物件例項的生命期設定隱式等待。
1 2 3 4 5 6 |
from selenium import webdriver driver = webdriver.Firefox() driver.implicitly_wait(10) # seconds driver.get("http://somedomain/url_that_delays_loading") myDynamicElement = driver.find_element_by_id("myDynamicElement") |
2.2.2:phantomjs的超時設定
這裡使用不帶selenium的phantomjs,需要使用js。主要設定語句是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
page.settings.resourceTimeout = 5000; // 等待5秒 var system = require('system'); var args = system.args; var url = args[1]; var page = require('webpage').create(); page.settings.resourceTimeout = 5000; // 等待5秒 page.onResourceTimeout = function(e) { console.log(e.errorCode); //列印錯誤碼 console.log(e.errorString);//列印錯誤語句 console.log(e.url); //列印錯誤url phantom.exit(1); }; page.open(url, function(status) { if(status==='success'){ var html=page.evaluate(function(){ returndocument.documentElement.outerHTML; }); console.log(html); } phantom.exit(); }); //$phantomjs xx.js http://bbs.pcbaby.com.cn/topic-2149414.html |
四:自定義函式的死鎖or超時處理
這個非常重要!!
python是順序執行的,但是如果下一句話可能導致死鎖(比如一個while(1))那麼如何強制讓他超時呢?他本身如果沒有帶有超時設定的話,就要自己執行訊號(import signal)來處理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#coding:utf-8 import time import signal def test(i): time.sleep(0.999)#模擬超時的情況 print "%d within time"%(i) return i def fuc_time(time_out): # 此為函式超時控制,替換下面的test函式為可能出現未知錯誤死鎖的函式 def handler(signum, frame): raise AssertionError try: signal.signal(signal.SIGALRM, handler) signal.alarm(time_out)#time_out為超時時間 temp = test(1) #函式設定部分,如果未超時則正常返回資料, return temp except AssertionError: print "%d timeout"%(i)# 超時則報錯 if __name__ == '__main__': for i in range(1,10): fuc_time(1) |
五:自定義執行緒的死鎖or超時處理
在某個程式中一方面不適合使用selenium+phantomjs的方式(要實現的功能比較難不適合)因為只能用原生的phantomjs,但是這個問題他本身在極端情況下也有可能停止(在超時設定之前因為某些錯誤)
那麼最佳方案就是用python單獨開一個執行緒(程式)呼叫原生phantomjs,然後對這個執行緒程式進行超時控制。
這裡用ping這個命令先做測試,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import subprocess from threading import Timer import time kill = lambda process: process.kill() cmd = ["ping", "www.google.com"] ping = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) my_timer = Timer(5, kill, [ping])#這裡設定時間,和命令 try: my_timer.start()#啟用 stdout, stderr = ping.communicate()#獲得輸出 #print stderr print time.ctime() finally: print time.ctime() my_timer.cancel() |
六:自重啟的程式設計
比如程式在某種情況下報錯多次,,那麼滿足條件後,讓其重啟即可解決大多數問題,當然這只不過是治標不治本而已,如果這個程式重啟沒有大問題(例如讀佇列型別)那麼自重啟這是最省力的方式之一。
1 2 3 4 5 6 7 8 9 10 11 12 |
import time import sys import os def restart_program(): python = sys.executable os.execl(python, python, * sys.argv) if __name__ == "__main__": print 'start...' print u"3秒後,程式將結束...".encode("utf8") time.sleep(3) restart_program() |
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!