《零基礎入門學習Python》第061講:論一隻爬蟲的自我修養9:異常處理
高階語言的一個有些特性就是它可以從容不迫的處理每一個遇到的錯誤,不至於說遇到一個小錯誤就導致整個程式崩潰了,大部分高階語言處理錯誤的方法都是通過檢測異常、處理異常來實現的,Python也是一樣。
用程式用程式碼進行網際網路訪問的時候,會出現異常是再正常不過的了,例如之前實現了一個代理程式,通過十幾個、幾十個代理 ip 來實現爬蟲訪問,如果說其中一個代理 ip 突然不響應了,那就會報錯,這種錯誤的觸發率是極高的,全部 ip 你都可以使用那才是有鬼呢。但是,出現一個代理 ip 不能用並不會影響到整個指令碼的任務,所以我們捕獲此類的異常,處理它的方法只需要忽略它就可以了。
我們今天就來談談訪問網頁的異常處理。
當我們的 urlopen() 方法無法處理一個響應的時候,就會引發一個 URLError 的異常,通常在沒有網路連線、或者對方伺服器壓根就不存在的時候,就會引發這個異常,同時,這個 URLError 的異常會同時伴隨給一個 reason 屬性,用於包含由錯誤編碼和錯誤資訊組成的元組。
我們舉個例子感受一下:(我們嘗試訪問一個不存在的域名:http://www.cug123.edu.cn/)
(URLError 是包含在 urllib.error 模組裡面的)
>>> import urllib.request >>> import urllib.error >>> req = urllib.request.Request("http://www.cug123.edu.cn/") >>> try: urllib.request.urlopen(req) except urllib.error.URLError as e: print(e.reason) [Errno 11001] getaddrinfo failed
錯誤號是 11001,伴隨的錯誤資訊是 獲取地址資訊失敗。(因為壓根沒有這個域名)
我們接下來看另一個,叫做 HTTPError
HTTPError 是 URLError 的一個子類,伺服器上每一個 http 的響應都會返回一個數字的狀態碼,例如我們看到的 404(表示說東西已經不存在了),有時候狀態碼會指出伺服器無法完成的請求型別,一般情況下,Python 會幫你處理一部分的這類請求,例如說響應一個 重定向,要求客戶端從別的地方來獲取文件,那麼這個 urllib 模組就會自動幫你處理這個響應,但是,再有一些情況下,它是無法幫你處理的,例如我們剛才說的 404,是頁面無法找到,它沒辦法幫你處理,需要你人工來進行過濾。也有 403(請求禁止),401(需要人工驗證)。我們這裡給大家準備好了
我們當出現一個錯誤的時候,伺服器就會返回一個 HTTP 錯誤號和錯誤的頁面,你可以使用 HTTPError 例項物件作為頁面返回的響應物件,它同樣也擁有 read() 和 geturl() 或者說info() 這類方法,我們來感受一下:
>>> import urllib.request
>>> import urllib.error
>>> req = urllib.request.Request("http://www.fishc.com/ooxx.html")
>>> try:
urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
print(e.code)
print(e.read())
print(e.reason)
執行結果如下:
404
b'<html>\r\n<head><title>404 Not Found</title></head>\r\n<body bgcolor="white">\r\n<center><h1>404 Not Found</h1></center>\r\n<hr><center>nginx</center>\r\n</body>\r\n</html>\r\n'
Not Found
最後給大家總結一下:
處理我們的異常有兩種寫法:
處理異常的第一種寫法:
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
req = Request(someurl)
try:
response = urlopen(req)
except HTTPError as e:
print('The server couldn\'t fulfill the request.')
print('Error code: ', e.code)
except URLError as e:
print('We failed to reach a server.')
print('Reason: ', e.reason)
else:
# everything is fine
注:這一種寫法有一點需要注意的就是:except HTTPError 必須寫在 except URLError 的前面,這樣才會響應到 HTTPError ,因為 HTTPError是 URLError 的子類,如果 except URLError 寫在前面,那麼 except HTTPError 永遠都響應不了。
處理異常的第二種寫法:
from urllib.request import Request, urlopen
from urllib.error import URLError
req = Request(someurl)
try:
response = urlopen(req)
except URLError as e:
if hasattr(e, 'reason'):
print('We failed to reach a server.')
print('Reason: ', e.reason)
if hasattr(e, 'code'):
print('The server couldn\'t fulfill the request.')
print('Error code: ', e.code)
else:
# everything is fine
第二種方法就是 先看有沒有錯誤(包括URLError 和 HTTPError ),只要有 其中一個,就會列印 reason, 然後繼續判斷是否有 code ,如果有 code,就是 HTTPError ,然後也把 code 打印出來。
舉例說明:
(一)這是一個 URLError 的域名。http://www.cug123.edu.cn/
>>> import urllib.request
>>> import urllib.error
>>> req = urllib.request.Request("http://www.cug123.edu.cn/")
>>> try:
urllib.request.urlopen(req)
except urllib.error.URLError as e:
if hasattr(e, 'reason'):
print('Reason: ', e.reason)
if hasattr(e, 'code'):
print('Error code: ', e.code)
Reason: [Errno 11001] getaddrinfo failed
(二)這是一個 HTTPError 的域名。http://www.fishc.com/ooxx.html
>>> import urllib.request
>>> import urllib.error
>>> req = urllib.request.Request("http://www.fishc.com/ooxx.html")
>>> try:
urllib.request.urlopen(req)
except urllib.error.URLError as e:
if hasattr(e, 'reason'):
print('Reason: ', e.reason)
if hasattr(e, 'code'):
print('Error code: ', e.code)
Reason: Not Found
Error code: 404