Can I set max_retries for requests.request?
Python请求模块简单而优雅,但有一点让我感到困惑。
可以使用以下消息获取requests.exception.ConnectionError:
1 | Max retries exceeded with url: ... |
这意味着请求可以尝试多次访问数据。 但是在文档的任何地方都没有提到这种可能性。 看看源代码,我找不到任何可以改变默认值(大概为0)的地方。
那么有可能以某种方式设置请求的最大重试次数吗?
这不仅会更改max_retries,还会启用退避策略,该策略会在重试之前使所有http://地址的请求休眠一段时间(总共5次):
1 2 3 4 5 6 7 8 9 10 11 12 13 | import requests from urllib3.util.retry import Retry from requests.adapters import HTTPAdapter s = requests.Session() retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[ 500, 502, 503, 504 ]) s.mount('http://', HTTPAdapter(max_retries=retries)) s.get('http://httpstat.us/500') |
根据
- total - 允许的重试总次数。
- connect - 要重试的与连接相关的错误数。
- 读取 - 重试读取错误的次数。
- 重定向 - 要执行的重定向数。
- method_whitelist - 我们应该重试的大写HTTP方法动词集。
- status_forcelist - 我们应该强制重试的一组HTTP状态代码。
- backoff_factor - 在尝试之间应用的退避因子。
-
raise_on_redirect - 是否,如果重定向的数量已用尽,则引发
MaxRetryError ,或返回响应代码在3xx范围内的响应。 - raise_on_status - 与raise_on_redirect类似的意思:如果状态落在status_forcelist范围内并且重试已经用尽,我们是应该引发异常还是返回响应。
注意:raise_on_status是相对较新的,并且尚未将其发送到urllib3或请求版本。 strike> raise_on_status关键字参数似乎最多在python版本3.6中进入标准库。
要使请求在特定HTTP状态代码上重试,请使用status_forcelist。例如,status_forcelist = [503]将在状态码503(服务不可用)上重试。
默认情况下,重试仅针对以下条件触发:
- 无法从池中获得连接。
-
TimeoutError -
HTTPException 引发(来自Python 3中的http.client,否则为httplib)。
这似乎是低级HTTP异常,如URL或协议
形成正确。 -
SocketError -
ProtocolError
请注意,这些都是阻止接收常规HTTP响应的异常。如果生成任何常规响应,则不会重试。如果不使用status_forcelist,即使状态为500的响应也不会被重试。
为了使其能够以更直观的方式使用远程API或Web服务器,我将使用上面的代码片段,它强制重试状态500,502,503和504,所有这些都不常见于在足够大的退避期间,网络和(可能)可恢复。
编辑:直接从urllib3导入
它是基础
1 2 3 4 | from requests.adapters import HTTPAdapter s = requests.Session() s.mount('http://stackoverflow.com', HTTPAdapter(max_retries=5)) |
旧的答案,早于请求的发布1.2.1:
1 2 3 | import requests requests.adapters.DEFAULT_RETRIES = 5 |
这个常数没有记录;因为未来版本可能会改变处理方式,所以使用它会让您自担风险。
更新:这确实改变了;在版本1.2.1中添加了在
小心,Martijn Pieters的答案不适合1.2.1+版本。如果不修补库,则无法全局设置。
你可以这样做:
1 2 3 4 5 6 | import requests from requests.adapters import HTTPAdapter s = requests.Session() s.mount('http://www.github.com', HTTPAdapter(max_retries=5)) s.mount('https://www.github.com', HTTPAdapter(max_retries=5)) |
在对这里的一些答案进行了一些努力之后,我找到了一个名为backoff的库,它对我的情况更有效。 一个基本的例子:
1 2 3 4 5 6 7 8 9 10 11 | import backoff @backoff.on_exception( backoff.expo, requests.exceptions.RequestException, max_tries=5, giveup=lambda e: e.response is not None and e.response.status_code < 500 ) def publish(self, data): r = requests.post(url, timeout=10, json=data) r.raise_for_status() |
我仍然建议尽快给出库的本机功能,但如果遇到任何问题或需要更广泛的控制,退避是一种选择。
获得更高控制的更简洁方法可能是将重试内容打包到函数中,并使用装饰器使该函数可重复,并将异常列入白名单。
我在这里创建了同样的东西:
重现该链接中的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | def retry(exceptions, delay=0, times=2): """ A decorator for retrying a function call with a specified delay in case of a set of exceptions Parameter List ------------- :param exceptions: A tuple of all exceptions that need to be caught for retry e.g. retry(exception_list = (Timeout, Readtimeout)) :param delay: Amount of delay (seconds) needed between successive retries. :param times: no of times the function should be retried """ def outer_wrapper(function): @functools.wraps(function) def inner_wrapper(*args, **kwargs): final_excep = None for counter in xrange(times): if counter > 0: time.sleep(delay) final_excep = None try: value = function(*args, **kwargs) return value except (exceptions) as e: final_excep = e pass #or log it if final_excep is not None: raise final_excep return inner_wrapper return outer_wrapper @retry(exceptions=(TimeoutError, ConnectTimeoutError), delay=0, times=3) def call_api(): |
1 2 3 4 5 | while page is None: try: page = requests.get(url, timeout=5,proxies=proxies) except Exception: page = None |