错误重试
错误重试用到的方法之一是:@retry()装饰器
装饰器实际是一个python函数,它的作用就是为被装饰的函数(或对象)进行装饰、包装,可以让被装饰的函数(或对象)在不需要做任何代码改动的情况下增加一些额外的功能,即被装饰函数(对象)的增强版。
安装
pipenv install retrying
应用语法
from retrying import retry
# @retry((指定重试的错误类型), 参数设置),例如
@retry((ValueError, TypeError), stop_max_attempt_number=5)
def func_name():
XXX
func_name()
常用的参数有:
1、stop_max_attempt_number:在停止之前尝试的最大次数,最后一次如果还是有异常则会抛出异常,停止运行,默认为5次
2、stop_max_delay:比如设置成10000,那么从被装饰的函数开始执行的时间点开始,到函数成功运行结束或者失败报错中止的时间点,只要这段时间超过10秒,则停止重试(单位是毫秒)
3、wait_fixed:设置在两次retrying之间的停留时间,单位毫秒
4、wait_random_min和wait_random_max:用随机的方式产生两次retrying之间的停留时间。wait_random_min停留最短时间,默认为0,单位毫秒。wait_random_max停留最长时间,默认为1000,单位毫秒
6、retry_on_result:指定一个函数,如果指定的函数返回True,则重试;否则抛出异常退出(指定要在得到哪些结果的时候去retry,retry_on_result传入一个函数对象,在执行get_result成功后,会将函数的返回值通过形参result的形式传入retry_if_result_none函数中,如果返回值是None那么就进行retry,否则就结束并返回函数值)
def retry_if_result_none(result):
return result is None
@retry(retry_on_result=retry_if_result_none)
def get_result():
print 'Retry forever ignoring Exceptions with no wait if return value is None'
return None
7、retry_on_exception: 指定一个函数,如果此函数返回指定异常,则会重试;如果不是指定的异常,则会退出
8、wait_exponential_multiplier和wait_exponential_max:以指数的形式产生两次retrying之间的停留时间,产生的值为2^previous_attempt_number * wait_exponential_multiplier, previous_attempt_number是前面已经retry的次数,如果产生的这个值超过了wait_exponential_max的大小,那么之后两个retrying之间的停留值都为wait_exponential_max
超时处理
爬虫可能会因为网络问题导致请求失败,或一直等待响应,这样会影响程序效率。为避免该问题,我们可以给requests的请求加上timeout参数,限制爬虫在一定的时间内需返回结果,否则就会报超时错误。
requests.get或requests.post——timeout参数的单位为秒,可以设置请求的超时时间,如果超出时间,则返回异常
response = requests.get(url, headers=headers, timeout=0.4)
自己编写超时函数装饰器
未完,待续。。。。
超时处理与retry装饰器的结合应用
爬虫请求中的timeout参数可以与retry装饰器结合使用。这样爬虫就可以避免偶然的网络波动导致的请求错误,会在设置的重试次数内重复发起请求。
import requests
from retrying import retry
# 全部报错才会报错,如果其中一次正常,则继续执行
# 两次retry之间等待2秒,重试5次
@retry(stop_max_attempt_number=5, wait_fixed=2000)
def get_request(url):
response = requests.get(url, headers=headers, timeout=1)
return response.content.decode()
headers = {"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"}
url = "https://www.baidu.com/"
print(get_request(url))
上面的例子,每次请求的超时限制是1秒,如果超过1秒未返回响应,则会报错。报错之后,会间隔2秒后再重新发起请求。只要其中一次正常,就会继续执行;否则,如果5次全部报错,才会报错。
捕捉异常
import requests
from retrying import retry
# 全部报错才会报错,如果其中一次正常,则继续执行
# 两次retry之间等待2秒,重试5次
@retry(stop_max_attempt_number=5, wait_fixed=1000)
def _get_request(url):
response = requests.get(url, headers=headers, timeout=1)
return response.content.decode()
def get_request(url):
try:
html_str = _get_request(url)
except TimeoutError: # 1
html_str = 'TimeoutError'
except: # 2
html_str = 'OtherError'
return html_str
headers = {"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"}
url = "https://www.baidu.com/"
print(get_request(url))
上面# 2处的except下面会出现“too broad exception clauses”(This inspection highlights too broad exception clauses such as no exception class specified, or specified as 'Exception'.)这是由于# 2处的except没有指定具体的报错类型,所以会出现exception过于宽泛的提示。
参考:
Python @retry装饰器的使用与实现案例(requests请求失败并重复请求)
如果觉得内容不错,请扫码关注微信公众号,获取更多内容