Python请求模块简单而优雅,但有一件事让我感到困扰。 有可能得到一个requests.exception.ConnectionError的消息,像这样:
Max retries exceeded with url: ...
这意味着请求可以多次尝试访问数据。但是在任何文件中都没有提到这种可能性。查看源代码,我没有找到任何可以更改默认值(假设为0)的地方。
那么是否有可能设置请求的最大重试次数呢?
Python请求模块简单而优雅,但有一件事让我感到困扰。 有可能得到一个requests.exception.ConnectionError的消息,像这样:
Max retries exceeded with url: ...
这意味着请求可以多次尝试访问数据。但是在任何文件中都没有提到这种可能性。查看源代码,我没有找到任何可以更改默认值(假设为0)的地方。
那么是否有可能设置请求的最大重试次数呢?
当前回答
您可以使用请求库一次性完成所有任务。 如果您收到429,500,502,503或504状态码,则以下代码将重试3次,每次通过“backoff_factor”设置更长的延迟。https://findwork.dev/blog/advanced-usage-python-requests-timeouts-retries-hooks/上有个不错的教程。
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
method_whitelist=["HEAD", "GET", "OPTIONS"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
http = requests.Session()
http.mount("https://", adapter)
http.mount("http://", adapter)
response = http.get("https://en.wikipedia.org/w/api.php")
其他回答
在苦苦思索了一些答案之后,我找到了一个叫做backoff的库,它更适合我的情况。一个基本的例子:
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()
我仍然建议尝试一下库的原生功能,但如果遇到任何问题或需要更广泛的控制,后退也是一种选择。
执行重试的是底层urllib3库。要设置不同的最大重试计数,请使用替代传输适配器:
from requests.adapters import HTTPAdapter
s = requests.Session()
s.mount('http://stackoverflow.com', HTTPAdapter(max_retries=5))
max_retries参数接受一个整数或Retry()对象;后者为您提供了重试失败类型的细粒度控制(将整数值转换为仅处理连接失败的Retry()实例;默认情况下,连接后的错误不会被处理,因为这些错误可能会导致副作用)。
旧的答案,在请求1.2.1发布之前:
请求库并没有真正做到这一点,也没有打算这样做(请参阅此pull request)。目前(请求1.1),重试计数设置为0。如果你真的想设置一个更高的值,你必须全局设置这个:
import requests
requests.adapters.DEFAULT_RETRIES = 5
这个常数没有记录;使用它的风险由您自己承担,因为未来的版本可能会改变这种处理方式。
更新:这确实改变了;在1.2.1版本中,添加了在HTTPAdapter()类上设置max_retries参数的选项,因此现在您必须使用替代传输适配器,参见上面。猴子补丁方法不再有效,除非你也修补HTTPAdapter.__init__()默认值(非常不推荐)。
注意,Martijn Pieters的答案不适合1.2.1+版本。如果不给库打补丁,就不能全局设置它。
你可以这样做:
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))
您可以使用请求库一次性完成所有任务。 如果您收到429,500,502,503或504状态码,则以下代码将重试3次,每次通过“backoff_factor”设置更长的延迟。https://findwork.dev/blog/advanced-usage-python-requests-timeouts-retries-hooks/上有个不错的教程。
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
method_whitelist=["HEAD", "GET", "OPTIONS"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
http = requests.Session()
http.mount("https://", adapter)
http.mount("http://", adapter)
response = http.get("https://en.wikipedia.org/w/api.php")
获得更高控制的一种更干净的方法可能是将重试内容打包到函数中,并使用装饰器使该函数可检索,并将异常白名单。
我在这里创建了相同的: http://www.praddy.in/retry-decorator-whitelisted-exceptions/
在该链接中重现代码:
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():