2020年9月12日星期六

《零基礎入門學習Python》第061講:論一隻爬蟲的自我修養9:異常處理

《零基礎入門學習Python》第061講:論一隻爬蟲的自我修養9:異常處理


高級語言的一個有些特性就是它可以從容不迫的處理每一個遇到的錯誤,不至於說遇到一個小錯誤就導致整個程序崩潰了,大部分高級語言處理錯誤的方法都是通過檢測異常、處理異常來實現的,Python也是一樣。
用程序用代碼進行互聯網訪問的時候,會出現異常是再正常不過的了,例如之前實現了一個代理程序,通過十幾個、幾十個代理ip 來實現爬蟲訪問,如果說其中一個代理ip 突然不響應了,那就會報錯,這種錯誤的觸發率是極高的,全部ip 你都可以使用那才是有鬼呢。但是,出現一個代理ip 不能用並不會影響到整個腳本的任務,所以我們捕獲此類的異常,處理它的方法只需要忽略它就可以了。

我們今天就來談談訪問網頁的異常處理。

當我們的urlopen() 方法無法處理一個響應的時候,就會引發一個URLError 的異常,通常在沒有網絡連接、或者對方服務器壓根就不存在的時候,就會引發這個異常,同時,這個URLError 的異常會同時伴隨給一個reason 屬性,用於包含由錯誤編碼和錯誤信息組成的元組。
我們舉個例子感受一下:(我們嘗試訪問一個不存在的域名:http://www.cug123.edu.cn/
(URLError 是包含在urllib.error 模塊裡面的)
  1. >>> import urllib.request
  2. >>> import urllib.error
  3. >>> req = urllib.request.Request("http://www.cug123.edu.cn/")
  4. >>> try:
  5. urllib.request.urlopen(req)
  6. except urllib.error.URLError as e:
  7. print(e.reason)
  8. [Errno 11001] getaddrinfo failed
錯誤號是11001,伴隨的錯誤信息是獲取地址信息失敗。(因為壓根沒有這個域名)
我們接下來看另一個,叫做HTTPError
HTTPError是URLError的一個子類,服務器上每一個http的響應都會返回一個數字的狀態碼,例如我們看到的404(表示說東西已經不存在了),有時候狀態碼會指出服務器無法完成的請求類型,一般情況下,Python會幫你處理一部分的這類請求,例如說響應一個重定向,要求客戶端從別的地方來獲取文檔,那麼這個urllib模塊就會自動幫你處理這個響應,但是,再有一些情況下,它是無法幫你處理的,例如我們剛才說的404,是頁面無法找到,它沒辦法幫你處理,需要你人工來進行過濾。也有403(請求禁止),401(需要人工驗證)。我們這裡給大家準備好了HTTP的狀態碼大全
我們當出現一個錯誤的時候,服務器就會返回一個HTTP 錯誤號和錯誤的頁面,你可以使用HTTPError 實例對像作為頁面返回的響應對象,它同樣也擁有read() 和geturl() 或者說info()這類方法,我們來感受一下:
  1. >>> import urllib.request
  2. >>> import urllib.error
  3. >>> req = urllib.request.Request("http://www.fishc.com/ooxx.html")
  4. >>> try:
  5. urllib.request.urlopen(req)
  6. except urllib.error.HTTPError as e:
  7. print(e.code)
  8. print(e.read())
  9. print(e.reason)
 運行結果如下:
  1. 404
  2. 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'
  3. Not Found

最後給大家總結一下:

處理我們的異常有兩種寫法:
處理異常的第一種寫法:
  1. from urllib.request import Request, urlopen
  2. from urllib.error import URLError, HTTPError
  3. req = Request(someurl)
  4. try:
  5. response = urlopen(req)
  6. except HTTPError as e:
  7. print('The server couldn\'t fulfill the request.')
  8. print('Error code: ', e.code)
  9. except URLError as e:
  10. print('We failed to reach a server.')
  11. print('Reason: ', e.reason)
  12. else:
  13. # everything is fine
注:這一種寫法有一點需要注意的就是:except HTTPError 必須寫在except URLError 的前面,這樣才會響應到HTTPError ,因為HTTPError是URLError 的子類,如果except URLError 寫在前面,那麼except HTTPError 永遠都響應不了。
處理異常的第二種寫法:
  1. from urllib.request import Request, urlopen
  2. from urllib.error import URLError
  3. req = Request(someurl)
  4. try:
  5. response = urlopen(req)
  6. except URLError as e:
  7. if hasattr(e, 'reason'):
  8. print('We failed to reach a server.')
  9. print('Reason: ', e.reason)
  10. if hasattr(e, 'code'):
  11. print('The server couldn\'t fulfill the request.')
  12. print('Error code: ', e.code)
  13. else:
  14. # everything is fine

第二種方法就是先看有沒有錯誤(包括URLError 和HTTPError ),只要有其中一個,就會打印reason, 然後繼續判斷是否有code ,如果有code,就是 HTTPError ,然後也把code 打印出來。

舉例說明:
(一)這是一個  URLError的域名。http://www.cug123.edu.cn/
  1. >>> import urllib.request
  2. >>> import urllib.error
  3. >>> req = urllib.request.Request("http://www.cug123.edu.cn/")
  4. >>> try:
  5. urllib.request.urlopen(req)
  6. except urllib.error.URLError as e:
  7. if hasattr(e, 'reason'):
  8. print('Reason: ', e.reason)
  9. if hasattr(e, 'code'):
  10. print('Error code: ', e.code)
  11. Reason: [Errno 11001] getaddrinfo failed
(二)這是一個HTTP Error的域名。http://www.fishc.com/ooxx.html
  1. >>> import urllib.request
  2. >>> import urllib.error
  3. >>> req = urllib.request.Request("http://www.fishc.com/ooxx.html")
  4. >>> try:
  5. urllib.request.urlopen(req)
  6. except urllib.error.URLError as e:
  7. if hasattr(e, 'reason'):
  8. print('Reason: ', e.reason)
  9. if hasattr(e, 'code'):
  10. print('Error code: ', e.code)
  11. Reason: Not Found
  12. Error code: 404

推薦使用第二種方法。


0 留言:

發佈留言