python 異常捕獲處理2019-07-10

調試Python程序時,經常會報出一些異常,異常的原因一方面可能是寫程序時由于疏忽或者考慮不全造成了錯誤,這時就需要根據異常Traceback到出錯點,進行分析改正;另一方面,有些異常是不可避免的,但我們可以對異常進行捕獲處理,防止程序終止。

1 異常類型
1.1 Python內置異常
Python的異常處理能力是很強大的,它有很多內置異常,可向用戶準確反饋出錯信息。在Python中,異常也是對象,可對它進行操作。BaseException是所有內置異常的基類,但用戶定義的類并不直接繼承BaseException,所有的異常類都是從Exception繼承,且都在exceptions模塊中定義。Python自動將所有異常名稱放在內建命名空間中,所以程序不必導入exceptions模塊即可使用異常。一旦引發而且沒有捕捉SystemExit異常,程序執行就會終止。如果交互式會話遇到一個未被捕捉的SystemExit異常,會話就會終止。

內置異常類的層次結構如下:

BaseException # 所有異常的基類

 +-- SystemExit  # 解釋器請求退出
 +-- KeyboardInterrupt  # 用戶中斷執行(通常是輸入^C)
 +-- GeneratorExit  # 生成器(generator)發生異常來通知退出
 +-- Exception  # 常規異常的基類
  +-- StopIteration  # 迭代器沒有更多的值
  +-- StopAsyncIteration  # 必須通過異步迭代器對象的__anext__()方法引發以停止迭代
  +-- ArithmeticError  # 各種算術錯誤引發的內置異常的基類
  |    +-- FloatingPointError  # 浮點計算錯誤
  |    +-- OverflowError  # 數值運算結果太大無法表示
  |    +-- ZeroDivisionError  # 除(或取模)零 (所有數據類型)
  +-- AssertionError  # 當assert語句失敗時引發
  +-- AttributeError  # 屬性引用或賦值失敗
  +-- BufferError  # 無法執行與緩沖區相關的操作時引發
  +-- EOFError  # 當input()函數在沒有讀取任何數據的情況下達到文件結束條件(EOF)時引發
  +-- ImportError  # 導入模塊/對象失敗
  |    +-- ModuleNotFoundError  # 無法找到模塊或在在sys.modules中找到None
  +-- LookupError  # 映射或序列上使用的鍵或索引無效時引發的異常的基類
  |    +-- IndexError  # 序列中沒有此索引(index)
  |    +-- KeyError  # 映射中沒有這個鍵
  +-- MemoryError  # 內存溢出錯誤(對于Python 解釋器不是致命的)
  +-- NameError  # 未聲明/初始化對象 (沒有屬性)
  |    +-- UnboundLocalError  # 訪問未初始化的本地變量
  +-- OSError  # 操作系統錯誤,EnvironmentError,IOError,WindowsError,socket.error,select.error和mmap.error已合并到OSError中,構造函數可能返回子類
  |    +-- BlockingIOError  # 操作將阻塞對象(e.g. socket)設置為非阻塞操作
  |    +-- ChildProcessError  # 在子進程上的操作失敗
  |    +-- ConnectionError  # 與連接相關的異常的基類
  |    |    +-- BrokenPipeError  # 另一端關閉時嘗試寫入管道或試圖在已關閉寫入的套接字上寫入
  |    |    +-- ConnectionAbortedError  # 連接嘗試被對等方中止
  |    |    +-- ConnectionRefusedError  # 連接嘗試被對等方拒絕
  |    |    +-- ConnectionResetError    # 連接由對等方重置
  |    +-- FileExistsError  # 創建已存在的文件或目錄
  |    +-- FileNotFoundError  # 請求不存在的文件或目錄
  |    +-- InterruptedError  # 系統調用被輸入信號中斷
  |    +-- IsADirectoryError  # 在目錄上請求文件操作(例如 os.remove())
  |    +-- NotADirectoryError  # 在不是目錄的事物上請求目錄操作(例如 os.listdir())
  |    +-- PermissionError  # 嘗試在沒有足夠訪問權限的情況下運行操作
  |    +-- ProcessLookupError  # 給定進程不存在
  |    +-- TimeoutError  # 系統函數在系統級別超時
  +-- ReferenceError  # weakref.proxy()函數創建的弱引用試圖訪問已經垃圾回收了的對象
  +-- RuntimeError  # 在檢測到不屬于任何其他類別的錯誤時觸發
  |    +-- NotImplementedError  # 在用戶定義的基類中,抽象方法要求派生類重寫該方法或者正在開發的類指示仍然需要添加實際實現
  |    +-- RecursionError  # 解釋器檢測到超出最大遞歸深度
  +-- SyntaxError  # Python 語法錯誤
  |    +-- IndentationError  # 縮進錯誤
  |         +-- TabError  # Tab和空格混用
  +-- SystemError  # 解釋器發現內部錯誤
  +-- TypeError  # 操作或函數應用于不適當類型的對象
  +-- ValueError  # 操作或函數接收到具有正確類型但值不合適的參數
  |    +-- UnicodeError  # 發生與Unicode相關的編碼或解碼錯誤
  |         +-- UnicodeDecodeError  # Unicode解碼錯誤
  |         +-- UnicodeEncodeError  # Unicode編碼錯誤
  |         +-- UnicodeTranslateError  # Unicode轉碼錯誤
  +-- Warning  # 警告的基類
       +-- DeprecationWarning  # 有關已棄用功能的警告的基類
       +-- PendingDeprecationWarning  # 有關不推薦使用功能的警告的基類
       +-- RuntimeWarning  # 有關可疑的運行時行為的警告的基類
       +-- SyntaxWarning  # 關于可疑語法警告的基類
       +-- UserWarning  # 用戶代碼生成警告的基類
       +-- FutureWarning  # 有關已棄用功能的警告的基類
       +-- ImportWarning  # 關于模塊導入時可能出錯的警告的基類
       +-- UnicodeWarning  # 與Unicode相關的警告的基類
       +-- BytesWarning  # 與bytes和bytearray相關的警告的基類
       +-- ResourceWarning  # 與資源使用相關的警告的基類。被默認警告過濾器忽略。

詳細說明請參考:https://docs.python.org/3/library/exceptions.html#base-classes

1.2 requests模塊的相關異常
在做爬蟲時,requests是一個十分好用的模塊,所以我們在這里專門探討一下requests模塊相關的異常。

要調用requests模塊的內置異常,只要“from requests.exceptions import xxx”就可以了,比如:

from requests.exceptions import ConnectionError, ReadTimeout
或者直接這樣也是可以的:

from requests import ConnectionError, ReadTimeout
requests模塊內置異常類的層次結構如下:

IOError

 +-- RequestException  # 處理不確定的異常請求
  +-- HTTPError  # HTTP錯誤
  +-- ConnectionError  # 連接錯誤
  |    +-- ProxyError  # 代理錯誤
  |    +-- SSLError  # SSL錯誤
  |    +-- ConnectTimeout(+-- Timeout)  # (雙重繼承,下同)嘗試連接到遠程服務器時請求超時,產生此錯誤的請求可以安全地重試。
  +-- Timeout  # 請求超時
  |    +-- ReadTimeout  # 服務器未在指定的時間內發送任何數據
  +-- URLRequired  # 發出請求需要有效的URL
  +-- TooManyRedirects  # 重定向太多
  +-- MissingSchema(+-- ValueError) # 缺少URL架構(例如http或https)
  +-- InvalidSchema(+-- ValueError) # 無效的架構,有效架構請參見defaults.py
  +-- InvalidURL(+-- ValueError)  # 無效的URL
  |    +-- InvalidProxyURL  # 無效的代理URL
  +-- InvalidHeader(+-- ValueError)  # 無效的Header
  +-- ChunkedEncodingError  # 服務器聲明了chunked編碼但發送了一個無效的chunk
  +-- ContentDecodingError(+-- BaseHTTPError)  # 無法解碼響應內容
  +-- StreamConsumedError(+-- TypeError)  # 此響應的內容已被使用
  +-- RetryError  # 自定義重試邏輯失敗
  +-- UnrewindableBodyError  # 嘗試倒回正文時,請求遇到錯誤
  +-- FileModeWarning(+-- DeprecationWarning)  # 文件以文本模式打開,但Requests確定

其二進制長度

  +-- RequestsDependencyWarning  # 導入的依賴項與預期的版本范圍不匹配

Warning

 +-- RequestsWarning  # 請求的基本警告

詳細說明及源碼請參考:http://www.python-requests.org/en/master/_modules/requests/exceptions/#RequestException

下面是一個簡單的小例子,python內置了一個ConnectionError異常,這里可以不用再從requests模塊import了:

import requests
from requests import ReadTimeout

def get_page(url):
try:
response = requests.get(url, timeout=1)
if response.status_code == 200:
return response.text
else:
print('Get Page Failed', response.status_code)
return None
except (ConnectionError, ReadTimeout):
print('Crawling Failed', url)
return None

def main():
url = 'https://www.baidu.com'
print(get_page(url))

if name == 'main':
main()

1.3 用戶自定義異常
此外,你也可以通過創建一個新的異常類擁有自己的異常,異常應該是通過直接或間接的方式繼承自Exception類。下面創建了一個MyError類,基類為Exception,用于在異常觸發時輸出更多的信息。

在try語句塊中,拋出用戶自定義的異常后執行except部分,變量 e 是用于創建MyError類的實例。

class MyError(Exception):
def init(self, msg):
self.msg = msg

def __str__(self):
    return self.msg

try:
raise MyError('類型錯誤')
except MyError as e:
print('My exception occurred', e.msg)

  1. 異常捕獲
    當發生異常時,我們就需要對異常進行捕獲,然后進行相應的處理。python的異常捕獲常用try...except...結構,把可能發生錯誤的語句放在try模塊里,用except來處理異常,每一個try,都必須至少對應一個except。此外,與python異常相關的關鍵字主要有:

關鍵字 關鍵字說明
try/except 捕獲異常并處理
pass 忽略異常
as 定義異常實例(except MyError as e)
else 如果try中的語句沒有引發異常,則執行else中的語句
finally 無論是否出現異常,都執行的代碼
raise 拋出/引發異常
異常捕獲有很多方式,下面分別進行討論。

2.1 捕獲所有異常
包括鍵盤中斷和程序退出請求(用sys.exit()就無法退出程序了,因為異常被捕獲了),因此慎用。

try:
<語句>

except:

  print('異常說明')

2.2 捕獲指定異常
try:
<語句>

except <異常名>:

  print('異常說明')

萬能異常:

try:
<語句>

except Exception:

  print('異常說明')

一個例子:

try:
f = open("file-not-exists", "r")

except IOError as e:

print("open exception: %s: %s" %(e.errno, e.strerror))

2.3 捕獲多個異常
捕獲多個異常有兩種方式,第一種是一個except同時處理多個異常,不區分優先級:

try:
<語句>

except (<異常名1>, <異常名2>, ...):

  print('異常說明')

第二種是區分優先級的:

try:
<語句>

except <異常名1>:

  print('異常說明1')

except <異常名2>:

  print('異常說明2')

except <異常名3>:

  print('異常說明3')

該種異常處理語法的規則是:

執行try下的語句,如果引發異常,則執行過程會跳到第一個except語句。
如果第一個except中定義的異常與引發的異常匹配,則執行該except中的語句。
如果引發的異常不匹配第一個except,則會搜索第二個except,允許編寫的except數量沒有限制。
如果所有的except都不匹配,則異常會傳遞到下一個調用本代碼的最高層try代碼中。
2.4 異常中的else
如果判斷完沒有某些異常之后還想做其他事,就可以使用下面這樣的else語句。

try:
<語句>

except <異常名1>:

  print('異常說明1')

except <異常名2>:

  print('異常說明2')

else:

  <語句>  # try語句中沒有異常則執行此段代碼

2.5 異常中的finally
try...finally...語句無論是否發生異常都將會執行最后的代碼。

try:
<語句>

finally:

  <語句>

看一個示例:

str1 = 'hello world'
try:
int(str1)
except IndexError as e:
print(e)
except KeyError as e:
print(e)
except ValueError as e:
print(e)
else:
print('try內沒有異常')
finally:
print('無論異常與否,都會執行我')
2.6 raise主動觸發異常
可以使用raise語句自己觸發異常,raise語法格式如下:

raise [Exception [, args [, traceback]]]
語句中Exception是異常的類型(例如ValueError),參數是一個異常參數值。該參數是可選的,如果不提供,異常的參數是"None"。最后一個參數是跟蹤異常對象,也是可選的(在實踐中很少使用)。

看一個例子:

def not_zero(num):
try:
if num == 0:
raise ValueError('參數錯誤')
return num
except Exception as e:
print(e)

not_zero(0)

2.7 采用traceback模塊查看異常
發生異常時,Python能“記住”引發的異常以及程序的當前狀態。Python還維護著traceback(跟蹤)對象,其中含有異常發生時與函數調用堆棧有關的信息。記住,異常可能在一系列嵌套較深的函數調用中引發。程序調用每個函數時,Python會在“函數調用堆棧”的起始處插入函數名。一旦異常被引發,Python會搜索一個相應的異常處理程序。如果當前函數中沒有異常處理程序,當前函數會終止執行,Python會搜索當前函數的調用函數,并以此類推,直到發現匹配的異常處理程序,或者Python抵達主程序為止。這一查找合適的異常處理程序的過程就稱為“堆棧輾轉開解”(StackUnwinding)。解釋器一方面維護著與放置堆棧中的函數有關的信息,另一方面也維護著與已從堆棧中“輾轉開解”的函數有關的信息。

格式如下:

try:
block

except:

traceback.print_exc()

舉個栗子:

try:
1/0
except Exception as e:
print(e)
如果我們這樣寫的話,程序只會報“division by zero”錯誤,但是我們并不知道是在哪個文件哪個函數哪一行出的錯。

下面使用traceback模塊,官方參考文檔:https://docs.python.org/2/library/traceback.html

import traceback

try:
1/0
except Exception as e:
traceback.print_exc()
這樣就會幫我們追溯到出錯點:

Traceback (most recent call last):
File "E:/PycharmProjects/ProxyPool-master/proxypool/test.py", line 4, in <module>
1/0
ZeroDivisionError: division by zero
另外,traceback.print_exc()跟traceback.format_exc()有什么區別呢?

區別就是,format_exc()返回字符串,print_exc()則直接給打印出來。即traceback.print_exc()與print(traceback.format_exc())效果是一樣的。print_exc()還可以接受file參數直接寫入到一個文件。比如可以像下面這樣把相關信息寫入到tb.txt文件去。

traceback.print_exc(file=open('tb.txt','w+'))

作者:polyhedronx
來源:CSDN
原文:https://blog.csdn.net/polyhedronx/article/details/81589196
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容