Python重試模組retrying
工作中經常碰到的問題就是,某個方法出現了異常,重試幾次。迴圈重複一個方法是很常見的。比如爬蟲中的獲取代理,對獲取失敗的情況進行重試。
剛開始搜的幾個部落格講的有點問題,建議看官方文件,還有自己動手實驗。
參考:
https://segmentfault.com/a/1190000004085023
https://pypi.org/project/retrying/
最初的版本
import requests
class ProxyUtil:
def __init__(self):
self._get_proxy_count = 0
def get_proxies(self):
try:
r = requests.get('代理伺服器地址')
# print('正在獲取')
# raise Exception("異常")
# print('獲取到最新代理 = %s' % r.text)
params = dict()
if r and r.status_code == 200:
proxy = str(r.content, encoding='utf-8')
params['http'] = 'http://' + proxy
params['https'] = 'https://' + proxy
else:
raise Exception("獲取代理失敗,狀態碼%s"%(r.status_code))
return params
except Exception:
if self._get_proxy_count < 5:
print('第%d次獲取代理失敗,準備重試' % self._get_proxy_count)
self._get_proxy_count += 1
self.get_proxies()
else:
print('第%d次獲取代理失敗,退出' % self._get_proxy_count)
self._get_proxy_count = 0
return dict()
if __name__ == '__main__':
proxy = ProxyUtil()
proxy.get_proxies()
以上程式碼通過
try...except...
捕獲異常,並通過一個計數器判斷獲取代理的次數,獲取失敗遞迴呼叫自己,直到達到最大次數為止。
為了模擬失敗,可以解開丟擲異常的註釋
下面來試試retrying模組
安裝
pip install retrying
retrying提供一個裝飾器函式retry,被裝飾的函式會在執行失敗的情況下重新執行,預設一直報錯就一直重試。
import requests
from retrying import retry
class ProxyUtil:
def __init__(self):
self._get_proxy_count = 0
@retry
def get_proxies(self):
r = requests.get('代理地址')
print('正在獲取')
raise Exception("異常")
print('獲取到最新代理 = %s' % r.text)
params = dict()
if r and r.status_code == 200:
proxy = str(r.content, encoding='utf-8')
params['http'] = 'http://' + proxy
params['https'] = 'https://' + proxy
if __name__ == '__main__':
proxy = ProxyUtil()
proxy.get_proxies()
結果:
正在獲取
正在獲取
正在獲取
...
正在獲取(一直重複下去)
沒有新增任何引數,預設情況下會一直重試,沒有等待時間
# 設定最大重試次數
@retry(stop_max_attempt_number=5)
def get_proxies(self):
r = requests.get('代理地址')
print('正在獲取')
raise Exception("異常")
print('獲取到最新代理 = %s' % r.text)
params = dict()
if r and r.status_code == 200:
proxy = str(r.content, encoding='utf-8')
params['http'] = 'http://' + proxy
params['https'] = 'https://' + proxy
# 設定方法的最大延遲時間,預設為100毫秒(是執行這個方法重試的總時間)
@retry(stop_max_attempt_number=5,stop_max_delay=50)
# 通過設定為50,我們會發現,任務並沒有執行5次才結束!
# 新增每次方法執行之間的等待時間
@retry(stop_max_attempt_number=5,wait_fixed=2000)
# 隨機的等待時間
@retry(stop_max_attempt_number=5,wait_random_min=100,wait_random_max=2000)
# 每呼叫一次增加固定時長
@retry(stop_max_attempt_number=5,wait_incrementing_increment=1000)
# 根據異常重試,先看個簡單的例子
def retry_if_io_error(exception):
return isinstance(exception, IOError)
@retry(retry_on_exception=retry_if_io_error)
def read_a_file():
with open("file", "r") as f:
return f.read()
read_a_file
函式如果丟擲了異常,會去retry_on_exception
指向的函式去判斷返回的是True
還是False
,如果是True
則執行指定的重試次數後,丟擲異常,False
的話直接丟擲異常。
當時自己測試的時候網上一大堆抄來抄去的,意思是retry_on_exception
指定一個函式,函式返回指定異常,會重試,不是異常會退出。真坑人啊!
來看看獲取代理的應用(僅僅是為了測試retrying模組)
# 定義一個函式用於判斷返回的是否是IOError
def wraper(args):
return isinstance(args,IOError)
class ProxyUtil:
def get_proxies(self):
r = requests.get('http://47.98.163.40:17000/get?country=local')
print('正在獲取')
raise IOError
# raise IndexError
print('獲取到最新代理 = %s' % r.text)
params = dict()
if r and r.status_code == 200:
proxy = str(r.content, encoding='utf-8')
params['http'] = 'http://' + proxy
params['https'] = 'https://' + proxy
# @retry_handler(retry_time=2, retry_interval=5, retry_on_exception=[IOError,IndexError])
@retry(stop_max_attempt_number=5,retry_on_exception=wraper)
def retry_test(self):
self.get_proxies()
print('io')
這種方法只能判斷單一的異常,而且擴充套件性不夠高
# 通過返回值判斷是否重試
def retry_if_result_none(result):
"""Return True if we should retry (in this case when result is None), False otherwise"""
# return result is None
if result =="111":
return True
@retry(stop_max_attempt_number=5,retry_on_result=retry_if_result_none)
def might_return_none():
print("Retry forever ignoring Exceptions with no wait if return value is None")
return "111"
might_return_none()
might_return_none
函式的返回值傳遞給retry_if_result_none
的result
,通過判斷result,返回Treu
或者None
表示需要重試,重試結束後丟擲RetryError
,返回False
表示不重試。
擴充套件預設的retry裝飾器:
def retry_handler(retry_time: int, retry_interval: float, retry_on_exception: [BaseException], *args, **kwargs):
def is_exception(exception: [BaseException]):
for exp in retry_on_exception:
if isinstance(exception,exp):
return True
return False
# return isinstance(exception, retry_on_exception)
def _retry(*args, **kwargs):
return Retrying(wait_fixed=retry_interval * 1000).fixed_sleep(*args, **kwargs)
return retry(
wait_func=_retry,
stop_max_attempt_number=retry_time,
retry_on_exception=is_exception
)
class ProxyUtil:
def get_proxies(self):
r = requests.get('代理地址')
print('正在獲取')
raise IOError
# raise IndexError
print('獲取到最新代理 = %s' % r.text)
params = dict()
if r and r.status_code == 200:
proxy = str(r.content, encoding='utf-8')
params['http'] = 'http://' + proxy
params['https'] = 'https://' + proxy
@retry_handler(retry_time=2, retry_interval=5, retry_on_exception=[IOError,IndexError])
# @retry(stop_max_attempt_number=5,retry_on_exception=wraper)
def retry_test(self):
self.get_proxies()
print('io')
if __name__ == '__main__':
proxy = ProxyUtil()
proxy.retry_test()