装饰器携带参数,可以实现更加强大的装饰器,本篇教程以http请求重试场景来讲解如何编写带参数的装饰器。
假设这样一个场景,你写了一个函数,用来从一个API接口获取数据,那么你必须考虑网络问题,也许网关解析会出错,也许API接口压力太大,短时间内响应超时,面对这种情况,你的函数应该具备重试功能。
请求失败以后,暂停一定时间,再次发起请求,重试的次数必须是可设置的。你可以把这个功能在函数中实现,但可以肯定,这不是一个好方法,原因在于每一个这样的函数你都需要实现一遍,而且不同的函数访问不同的API,暂停的时间以及重试的次数都有可能不同。
这种请求,就非常适合用装饰器来解决
from functools import wraps
def retry(retry_count=5, sleep_time=1):
def wrapper(func):
@wraps(func)
def inner(*args, **kwargs):
for i in range(retry_count):
try:
res = func(*args, **kwargs)
return res
except:
time.sleep(sleep_time)
continue
return None
return inner
return wrapper
这段代码一共嵌套着定义了3个函数,在讲解作用域的时候强调过,嵌套作用域是一个相对概念,在函数inner里可以访问更上层的局部作用域里的数据,retry_count是函数retry的一个参数,在inner里根据retry_count来决定重试的次数,只要发生了异常,就会重试。
写爬虫时,你不得不考虑网络因素,一次请求可能无法得到所需要的响应数据,这时就需要重试
class HttpCodeException(Exception):
pass
@retry()
def get_html(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.87 Mobile Safari/537.36',
'Host': 'movie.douban.com',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
'Referer': 'https://movie.douban.com/top250?start=25&filter='
}
res = requests.get(url, headers=headers)
if res.status_code != 200:
raise HttpCodeException
return res.text
这段代码是我编写过的一个爬虫的一小部分,在其他场景下,只要涉及到重试,都可以使用我写的这个装饰器。
QQ交流群: 211426309