調(diào)試Python程序時星掰,經(jīng)常會報出一些異常怎诫,異常的原因一方面可能是寫程序時由于疏忽或者考慮不全造成了錯誤善延,這時就需要根據(jù)異常Traceback到出錯點,進行分析改正惰聂;另一方面疆偿,有些異常是不可避免的,但我們可以對異常進行捕獲處理搓幌,防止程序終止杆故。
1 異常類型
1.1 Python內(nèi)置異常
Python的異常處理能力是很強大的,它有很多內(nèi)置異常溉愁,可向用戶準(zhǔn)確反饋出錯信息处铛。在Python中,異常也是對象,可對它進行操作撤蟆。BaseException是所有內(nèi)置異常的基類奕塑,但用戶定義的類并不直接繼承BaseException,所有的異常類都是從Exception繼承家肯,且都在exceptions模塊中定義龄砰。Python自動將所有異常名稱放在內(nèi)建命名空間中,所以程序不必導(dǎo)入exceptions模塊即可使用異常讨衣。一旦引發(fā)而且沒有捕捉SystemExit異常寝贡,程序執(zhí)行就會終止。如果交互式會話遇到一個未被捕捉的SystemExit異常值依,會話就會終止圃泡。
內(nèi)置異常類的層次結(jié)構(gòu)如下:
BaseException # 所有異常的基類
+-- SystemExit # 解釋器請求退出
+-- KeyboardInterrupt # 用戶中斷執(zhí)行(通常是輸入^C)
+-- GeneratorExit # 生成器(generator)發(fā)生異常來通知退出
+-- Exception # 常規(guī)異常的基類
+-- StopIteration # 迭代器沒有更多的值
+-- StopAsyncIteration # 必須通過異步迭代器對象的__anext__()方法引發(fā)以停止迭代
+-- ArithmeticError # 各種算術(shù)錯誤引發(fā)的內(nèi)置異常的基類
| +-- FloatingPointError # 浮點計算錯誤
| +-- OverflowError # 數(shù)值運算結(jié)果太大無法表示
| +-- ZeroDivisionError # 除(或取模)零 (所有數(shù)據(jù)類型)
+-- AssertionError # 當(dāng)assert語句失敗時引發(fā)
+-- AttributeError # 屬性引用或賦值失敗
+-- BufferError # 無法執(zhí)行與緩沖區(qū)相關(guān)的操作時引發(fā)
+-- EOFError # 當(dāng)input()函數(shù)在沒有讀取任何數(shù)據(jù)的情況下達到文件結(jié)束條件(EOF)時引發(fā)
+-- ImportError # 導(dǎo)入模塊/對象失敗
| +-- ModuleNotFoundError # 無法找到模塊或在在sys.modules中找到None
+-- LookupError # 映射或序列上使用的鍵或索引無效時引發(fā)的異常的基類
| +-- IndexError # 序列中沒有此索引(index)
| +-- KeyError # 映射中沒有這個鍵
+-- MemoryError # 內(nèi)存溢出錯誤(對于Python 解釋器不是致命的)
+-- NameError # 未聲明/初始化對象 (沒有屬性)
| +-- UnboundLocalError # 訪問未初始化的本地變量
+-- OSError # 操作系統(tǒng)錯誤,EnvironmentError愿险,IOError颇蜡,WindowsError,socket.error辆亏,select.error和mmap.error已合并到OSError中风秤,構(gòu)造函數(shù)可能返回子類
| +-- BlockingIOError # 操作將阻塞對象(e.g. socket)設(shè)置為非阻塞操作
| +-- ChildProcessError # 在子進程上的操作失敗
| +-- ConnectionError # 與連接相關(guān)的異常的基類
| | +-- BrokenPipeError # 另一端關(guān)閉時嘗試寫入管道或試圖在已關(guān)閉寫入的套接字上寫入
| | +-- ConnectionAbortedError # 連接嘗試被對等方中止
| | +-- ConnectionRefusedError # 連接嘗試被對等方拒絕
| | +-- ConnectionResetError # 連接由對等方重置
| +-- FileExistsError # 創(chuàng)建已存在的文件或目錄
| +-- FileNotFoundError # 請求不存在的文件或目錄
| +-- InterruptedError # 系統(tǒng)調(diào)用被輸入信號中斷
| +-- IsADirectoryError # 在目錄上請求文件操作(例如 os.remove())
| +-- NotADirectoryError # 在不是目錄的事物上請求目錄操作(例如 os.listdir())
| +-- PermissionError # 嘗試在沒有足夠訪問權(quán)限的情況下運行操作
| +-- ProcessLookupError # 給定進程不存在
| +-- TimeoutError # 系統(tǒng)函數(shù)在系統(tǒng)級別超時
+-- ReferenceError # weakref.proxy()函數(shù)創(chuàng)建的弱引用試圖訪問已經(jīng)垃圾回收了的對象
+-- RuntimeError # 在檢測到不屬于任何其他類別的錯誤時觸發(fā)
| +-- NotImplementedError # 在用戶定義的基類中,抽象方法要求派生類重寫該方法或者正在開發(fā)的類指示仍然需要添加實際實現(xiàn)
| +-- RecursionError # 解釋器檢測到超出最大遞歸深度
+-- SyntaxError # Python 語法錯誤
| +-- IndentationError # 縮進錯誤
| +-- TabError # Tab和空格混用
+-- SystemError # 解釋器發(fā)現(xiàn)內(nèi)部錯誤
+-- TypeError # 操作或函數(shù)應(yīng)用于不適當(dāng)類型的對象
+-- ValueError # 操作或函數(shù)接收到具有正確類型但值不合適的參數(shù)
| +-- UnicodeError # 發(fā)生與Unicode相關(guān)的編碼或解碼錯誤
| +-- UnicodeDecodeError # Unicode解碼錯誤
| +-- UnicodeEncodeError # Unicode編碼錯誤
| +-- UnicodeTranslateError # Unicode轉(zhuǎn)碼錯誤
+-- Warning # 警告的基類
+-- DeprecationWarning # 有關(guān)已棄用功能的警告的基類
+-- PendingDeprecationWarning # 有關(guān)不推薦使用功能的警告的基類
+-- RuntimeWarning # 有關(guān)可疑的運行時行為的警告的基類
+-- SyntaxWarning # 關(guān)于可疑語法警告的基類
+-- UserWarning # 用戶代碼生成警告的基類
+-- FutureWarning # 有關(guān)已棄用功能的警告的基類
+-- ImportWarning # 關(guān)于模塊導(dǎo)入時可能出錯的警告的基類
+-- UnicodeWarning # 與Unicode相關(guān)的警告的基類
+-- BytesWarning # 與bytes和bytearray相關(guān)的警告的基類
+-- ResourceWarning # 與資源使用相關(guān)的警告的基類扮叨。被默認(rèn)警告過濾器忽略缤弦。
詳細說明請參考:https://docs.python.org/3/library/exceptions.html#base-classes
1.2 requests模塊的相關(guān)異常
在做爬蟲時,requests是一個十分好用的模塊彻磁,所以我們在這里專門探討一下requests模塊相關(guān)的異常碍沐。
要調(diào)用requests模塊的內(nèi)置異常,只要“from requests.exceptions import xxx”就可以了衷蜓,比如:
from requests.exceptions import ConnectionError, ReadTimeout
或者直接這樣也是可以的:
from requests import ConnectionError, ReadTimeout
requests模塊內(nèi)置異常類的層次結(jié)構(gòu)如下:
IOError
+-- RequestException # 處理不確定的異常請求
+-- HTTPError # HTTP錯誤
+-- ConnectionError # 連接錯誤
| +-- ProxyError # 代理錯誤
| +-- SSLError # SSL錯誤
| +-- ConnectTimeout(+-- Timeout) # (雙重繼承累提,下同)嘗試連接到遠程服務(wù)器時請求超時,產(chǎn)生此錯誤的請求可以安全地重試磁浇。
+-- Timeout # 請求超時
| +-- ReadTimeout # 服務(wù)器未在指定的時間內(nèi)發(fā)送任何數(shù)據(jù)
+-- URLRequired # 發(fā)出請求需要有效的URL
+-- TooManyRedirects # 重定向太多
+-- MissingSchema(+-- ValueError) # 缺少URL架構(gòu)(例如http或https)
+-- InvalidSchema(+-- ValueError) # 無效的架構(gòu)斋陪,有效架構(gòu)請參見defaults.py
+-- InvalidURL(+-- ValueError) # 無效的URL
| +-- InvalidProxyURL # 無效的代理URL
+-- InvalidHeader(+-- ValueError) # 無效的Header
+-- ChunkedEncodingError # 服務(wù)器聲明了chunked編碼但發(fā)送了一個無效的chunk
+-- ContentDecodingError(+-- BaseHTTPError) # 無法解碼響應(yīng)內(nèi)容
+-- StreamConsumedError(+-- TypeError) # 此響應(yīng)的內(nèi)容已被使用
+-- RetryError # 自定義重試邏輯失敗
+-- UnrewindableBodyError # 嘗試倒回正文時,請求遇到錯誤
+-- FileModeWarning(+-- DeprecationWarning) # 文件以文本模式打開置吓,但Requests確定
其二進制長度
+-- RequestsDependencyWarning # 導(dǎo)入的依賴項與預(yù)期的版本范圍不匹配
Warning
+-- RequestsWarning # 請求的基本警告
詳細說明及源碼請參考:http://www.python-requests.org/en/master/_modules/requests/exceptions/#RequestException
下面是一個簡單的小例子无虚,python內(nèi)置了一個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 用戶自定義異常
此外衍锚,你也可以通過創(chuàng)建一個新的異常類擁有自己的異常友题,異常應(yīng)該是通過直接或間接的方式繼承自Exception類。下面創(chuàng)建了一個MyError類构拳,基類為Exception咆爽,用于在異常觸發(fā)時輸出更多的信息梁棠。
在try語句塊中,拋出用戶自定義的異常后執(zhí)行except部分斗埂,變量 e 是用于創(chuàng)建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)
- 異常捕獲
當(dāng)發(fā)生異常時,我們就需要對異常進行捕獲呛凶,然后進行相應(yīng)的處理男娄。python的異常捕獲常用try...except...結(jié)構(gòu),把可能發(fā)生錯誤的語句放在try模塊里漾稀,用except來處理異常模闲,每一個try,都必須至少對應(yīng)一個except崭捍。此外尸折,與python異常相關(guān)的關(guān)鍵字主要有:
關(guān)鍵字 關(guān)鍵字說明
try/except 捕獲異常并處理
pass 忽略異常
as 定義異常實例(except MyError as e)
else 如果try中的語句沒有引發(fā)異常,則執(zhí)行else中的語句
finally 無論是否出現(xiàn)異常殷蛇,都執(zhí)行的代碼
raise 拋出/引發(fā)異常
異常捕獲有很多方式实夹,下面分別進行討論。
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同時處理多個異常缴淋,不區(qū)分優(yōu)先級:
try:
<語句>
except (<異常名1>, <異常名2>, ...):
print('異常說明')
第二種是區(qū)分優(yōu)先級的:
try:
<語句>
except <異常名1>:
print('異常說明1')
except <異常名2>:
print('異常說明2')
except <異常名3>:
print('異常說明3')
該種異常處理語法的規(guī)則是:
執(zhí)行try下的語句,如果引發(fā)異常泄朴,則執(zhí)行過程會跳到第一個except語句重抖。
如果第一個except中定義的異常與引發(fā)的異常匹配,則執(zhí)行該except中的語句叼旋。
如果引發(fā)的異常不匹配第一個except仇哆,則會搜索第二個except,允許編寫的except數(shù)量沒有限制夫植。
如果所有的except都不匹配,則異常會傳遞到下一個調(diào)用本代碼的最高層try代碼中油讯。
2.4 異常中的else
如果判斷完沒有某些異常之后還想做其他事详民,就可以使用下面這樣的else語句。
try:
<語句>
except <異常名1>:
print('異常說明1')
except <異常名2>:
print('異常說明2')
else:
<語句> # try語句中沒有異常則執(zhí)行此段代碼
2.5 異常中的finally
try...finally...語句無論是否發(fā)生異常都將會執(zhí)行最后的代碼陌兑。
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內(nèi)沒有異常')
finally:
print('無論異常與否,都會執(zhí)行我')
2.6 raise主動觸發(fā)異常
可以使用raise語句自己觸發(fā)異常沈跨,raise語法格式如下:
raise [Exception [, args [, traceback]]]
語句中Exception是異常的類型(例如ValueError),參數(shù)是一個異常參數(shù)值兔综。該參數(shù)是可選的饿凛,如果不提供狞玛,異常的參數(shù)是"None"。最后一個參數(shù)是跟蹤異常對象涧窒,也是可選的(在實踐中很少使用)心肪。
看一個例子:
def not_zero(num):
try:
if num == 0:
raise ValueError('參數(shù)錯誤')
return num
except Exception as e:
print(e)
not_zero(0)
2.7 采用traceback模塊查看異常
發(fā)生異常時,Python能“記住”引發(fā)的異常以及程序的當(dāng)前狀態(tài)纠吴。Python還維護著traceback(跟蹤)對象硬鞍,其中含有異常發(fā)生時與函數(shù)調(diào)用堆棧有關(guān)的信息。記住戴已,異彻谈茫可能在一系列嵌套較深的函數(shù)調(diào)用中引發(fā)。程序調(diào)用每個函數(shù)時糖儡,Python會在“函數(shù)調(diào)用堆椃セ担”的起始處插入函數(shù)名。一旦異常被引發(fā)握联,Python會搜索一個相應(yīng)的異常處理程序著淆。如果當(dāng)前函數(shù)中沒有異常處理程序,當(dāng)前函數(shù)會終止執(zhí)行拴疤,Python會搜索當(dāng)前函數(shù)的調(diào)用函數(shù)永部,并以此類推,直到發(fā)現(xiàn)匹配的異常處理程序呐矾,或者Python抵達主程序為止苔埋。這一查找合適的異常處理程序的過程就稱為“堆棧輾轉(zhuǎn)開解”(StackUnwinding)。解釋器一方面維護著與放置堆棧中的函數(shù)有關(guān)的信息蜒犯,另一方面也維護著與已從堆棧中“輾轉(zhuǎn)開解”的函數(shù)有關(guān)的信息组橄。
格式如下:
try:
block
except:
traceback.print_exc()
舉個栗子:
try:
1/0
except Exception as e:
print(e)
如果我們這樣寫的話,程序只會報“division by zero”錯誤罚随,但是我們并不知道是在哪個文件哪個函數(shù)哪一行出的錯玉工。
下面使用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()有什么區(qū)別呢遵班?
區(qū)別就是,format_exc()返回字符串潮改,print_exc()則直接給打印出來狭郑。即traceback.print_exc()與print(traceback.format_exc())效果是一樣的。print_exc()還可以接受file參數(shù)直接寫入到一個文件汇在。比如可以像下面這樣把相關(guān)信息寫入到tb.txt文件去翰萨。
traceback.print_exc(file=open('tb.txt','w+'))
作者:polyhedronx
來源:CSDN
原文:https://blog.csdn.net/polyhedronx/article/details/81589196
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接糕殉!