【爬虫 | Python】解决'Requests Max Retries Exceeded With Url in Python'报错的问题
我们在写爬虫的时候,经常会遇到这样的报错信息:
HTTPConnectionPool(host=‘xxx.xxx.com’, port=443): Max retries exceeded with url: /api/v2/oauth (Caused by NewConnectionError(’
: Failed to establish a new connection: [Errno 110] Connection timed out’))
这里有3个重要的信息点,分别是:
HTTPConnectionPool(host='xxx.xxx.com', port=443)
Max retries exceeded with url:
Connection timed out
一、普遍方案
针对上述的问题,目前网上给出3种解决方案:
在Headers里添加Connection
参数,使其为close
,因为默认为keep-alive
。
因为requests使用的http连接池在发出request后会继续保持connection处于活跃状态,而http线程池里的的线程数是有限的。当多次request后,随机使用完了线程池里的session,因此为避免此问题需要设置connection为close来断连。
headers = {'Connection': 'close' # 设置为关闭长连接}
使用requests.close()
来关闭连接。
使用
self.close()
主动断开宇服务器的连接。
req = requests.get(url='xxx', headers='xxx', data='xxx)req.close()# 关闭连接
使用request.session()
的请求方式。
创建
session
,然后主动断开。
s = requests.session()s.keep_alive = False # 关闭多余连接s.get(url='xxx', headers='xxx', data='xxx)
上面的3种方案也许能解决问题,但是经过尝试,并非所有情况都能有效解决问题。下面给出更加全面和细致的解决方案。
细致方案
俗话说,具体问题具体分析。“因地制宜”才能真正解决所遇到的问题。
当请求库无法成功将请求发送到发出的站点时,就会发生错误。发生这种情况是因为不同的原因。这是常见的。其中可能存在如下的问题所致:
错误的 URL (解决方案 1)
2.未能验证 SSL 证书(解决方案 2)
3.使用没有或不稳定的互联网连接的请求(解决方案 3)
4.发送太多请求或服务器太忙(解决方案 4)
一、问题重述
1.错误的 URL
发生这种错误的其中一种可能的原因是你输入了错误的URL,导致request请求不到或超时。例如:
你把com
写成了con
,因此需要认真检查URL是否正确。
import requests # 错误的URL,应为https://www.github.com/host = 'https://www.github.con' res = requests.get(url=host)print(res) # 报错
你所调用的URL已失效或不存在,例如某个API已经失效或不存在,因此会发生此问题,可以使用浏览器检验一下该URL的有效性。
import requests # 不存在的URL或已失效的URLhost = 'https://www.github.com//api/baidu' res = requests.get(url=host)print(res) # 报错
上述问题输出:
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='www.github.coxxx', port=443): Max retries exceeded with url: / (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))
可以观察到报错信息的最后部分是
Connection refused
。
2.未能验证 SSL 证书
默认情况下,请求库实施 SSL 证书验证以确保您建立安全连接。如果无法验证证书,您最终会遇到如下错误:
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='www.github.coxxx', port=443): Max retries exceeded with url: / (Caused by NewConnectionError(': requests.exceptions.SSLError: [Errno 1] _ssl.c:503: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed'))
可以观察到报错信息的最后部分是
certificate verify failed
。
3.网络不稳定
你所发送请求的主机与目标服务器之间的通信路线不稳定(如跨国)或两者之间其中之一的带宽受限导致的网络不稳定现象所致:
Error: requests.exceptions.ConnectionError: HTTPSConnectionPool(host=’www.github.com’, port=443): Max retries exceeded with url: / (Caused by NewConnectionError(‘: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution’))
可以观察到报错信息的最后部分是
Temporary failure in name resolution
。
4.发送太多请求/服务器过载
当如此快地发出如此多的请求时,某些网站会阻止连接。与此相关的另一个问题是当服务器过载时——同时管理大量连接。或者你的IP已经被目标服务器加入了黑名单(被封锁IP)。在这种情况下,requests.get()
会抛出如下错误:
requests.exceptions.ConnectionError: HTTPConnectionPool(host='www.github.com', port=80): Max retries exceeded with url: / (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 11001] getaddrinfo failed'))
HTTPConnectionPool(host='www.github.com', port=80): Max retries exceeded with url: /api/v2/oauth (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 110] Connection timed out'))
二、应对方案
1.方案一:仔细检查 URL
确保您拥有正确且有效的 URL。考虑前面提到的有效 URL:“ https://www.github.com/ ”。“Max retries exceeded with url”错误主要发生在对www
和顶级域名(例如 .com
)进行错误编辑时。
当方案/协议 (https) 被错误编辑时会出现另一个错误:requests.exceptions.InvalidSchema。如果二级域(在我们的例子中是“github”)被错误编辑,我们将被引导到一个完全不同的网站,如果该网站不存在,我们会收到 404 响应。
导致“超过 url 的最大重试次数”的其他错误 URL 是“wwt.github.com”和“https://www.github.com/”(.com 之后的空格)。
2.方案二:解决SSLError
如前所述,该错误是由不受信任的 SSL 证书引起的。最快的解决方法是在requests.get()
上设置属性 verify=False
。这告诉 requests 在不验证 SSL 证书的情况下发送请求。
requests.get('https://www.github.com/', verify=False)
请注意,证书不会被验证;因此,您的应用程序将面临中间人攻击等安全威胁。对于在生产级别使用的脚本,最好避免使用此方法。
3.方案三:解决网络不稳定的问题
此解决方案适用于间歇性连接中断的情况。在这些情况下,我们希望请求能够在抛出错误之前对请求进行多次尝试。对于这种情况,我们可以使用两种解决方案:
- 在
requests.get()
中发出超时参数。- 在出现与连接相关的错误时重试连接。
方案A
在
requests.get()
中发出超时参数。
如果服务器过载,我们可以使用超时来等待更长时间的响应。这将增加请求成功完成的机会。
import requests url = 'https://www.github.com/'res = requests.get(url, timeout=7)print(res)
上面的代码将等待 7 秒,以便请求包连接到站点并读取源代码。
你可以将超时作为双元素元组传递,其中第一个元素是连接超时(与服务器建立连接的时间),第二个值是读取超时(允许客户端从服务器读取数据的时间)
requests.get('https://api.github.com', timeout=(3, 7))
使用以上线路时,必须在3秒内建立连接,7秒内读取数据;否则,请求会引发超时错误。
方案B
在出现连接相关错误时重试连接.
这些请求使用urllib3 中的重试实用程序 ( urllib3.util.Retry
) 来重试连接。我们将使用以下代码发送请求:
import requestsfrom requests.adapters import HTTPAdapter, Retryimport time def send_request(url, n_retries=4, backoff_factor=0.9, status_codes=[504, 503, 502, 500, 429]): sess = requests.Session() retries = Retry(connect=n_retries, backoff_factor=backoff_factor, status_forcelist=status_codes) sess.mount("https://", HTTPAdapter(max_retries=retries)) sess.mount("http://", HTTPAdapter(max_retries=retries)) res = sess.get(url) return res
我们在urllib3.util.Retry
类中使用了以下参数:
- connect – 与连接相关的尝试次数。默认情况下,
send_request()
将进行 4 次尝试加 1 次(立即发生的原始请求)。 - backoff_factor – 确定重试之间的延迟。休眠时间使用公式
{backoff_factor} * (2 ^ ({retry_number} – 1))
计算。我们将在调用该函数时处理此参数的示例。 - status_forcelist – 仅重试导致
504
、503
、502
、500
和429
状态代码的所有连接。
现在让我们调用我们的函数并计时执行。
# 开始一个计时器start_time = time.time() # 请求Github APIurl = "https://api.github.com/users"res = send_request(url) # 打印返回状态码print(res.status_code) # 结束计时器end_time = time.time() # 计算运行时长print("Run time: ", end_time-start_time)
输出:
200Run time: 0.8597214221954346
连接已成功完成(状态 200
),耗时 0.86 秒。要查看退避的实现,让我们尝试向不存在的服务器发送请求,在异常发生时捕获异常并计算执行时间。
try: # 启动执行定时器 start_time = time.time() url = "http://localhost/6000"# 调用send_request()方法向url发送请求 # 这永远不会成功,因为没有服务器在运行 # 在端口 6000 上。 response = send_request(url) print(response.status_code)except Exception as e: # 捕获任何异常 - 执行将在这里结束,因为 # 请求无法连接到 http://localhost/6000 print("Error Name: ", e.__class__.__name__) print("Error Message: ", e)finally: # 选择结束时间 end_time = time.time() # 计算执行所花费的时间 print("Run time: ", end_time-start_time)
输出:
Error Name: ConnectionErrorError Message: HTTPConnectionPool(host='localhost', port=80): Max retries exceeded with url: /6000 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))Run time: 12.61784315109253
在 backoff_factor=0.9
的 4 次重试(加上 1 个原始请求)之后,执行时间为 12.6 秒。让我们使用之前看到的公式来计算睡眠时间。
sleeping_time = {backoff_factor} * (2 ^ ({retry_number} – 1))
总共有5个请求
- 第一个请求(立即发出)——睡眠
0
秒, - 第一次重试(也是在第一次请求失败后立即发送)——
0
秒休眠, - 第二次重试->
0.9*(2^(2-1)) = 0.9*2 = 1.8
秒睡眠, - 第三次重试 ->
0.9*(2^(3-1)) = 0.9*4 = 3.6
秒的休眠时间,以及, - 第四次重试 ->
0.9*(2^(4-1)) = 0.9*8 = 7.2
秒。
这是
urllib3.util.Retry
实现的总共12.6
秒的休眠时间。实际执行时间为12.61784315109253
秒。未说明的0.01784315109253
差异归因于 DNC 和一般计算机电源延迟。
4.方案四:超时处理服务器请求
一些网站会阻止网络爬虫。他们注意到机器人正在根据传递的标头发送请求。例如,让我们运行此代码并打开详细信息以查看幕后发生的情况。
import http.client # turn verbose onhttp.client.HTTPConnection.debuglevel = 1import requestsurl = 'https://www.github.com/'res = requests.get(url=url)print(res)
输出(Curl):
send: b'GET / HTTP/1.1\r\nHost: www.github.com\r\nUser-Agent: python-requests/2.28.1\r\nAccept-Encoding: gzip, deflate\r\nAccept: **\r\nConnection: keep-alive\r\n\r\n'reply: 'HTTP/1.1 200 OK\r\n'
重要:
针对被高级反爬及被封禁IP的解决方法策略有所不同。
被高级反爬后的处理:
例如像国内rs这样的中高级动态反爬技术,其原理是检测用户浏览器环境是否为真实的用户环境。
针对此类问题需要采用更加高级的手段进行沙盒实验及补环境。
所使用的设备被目标主机封禁IP。
针对此类问题,可以使用IP代理池,每次请求都随机更换IP来避免被封禁;其次,需要降低请求的速率。
注意:请遵守所在国家或地区的法律法规。
本文首发于我的个人博客: Colyn’s Blog,欢迎访问和留言!
本文属于作者自主知识产权的原创文章,如须引用、转载,请注明来源:
版权声明:本文为CSDN博主「InetGeek」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34532102/article/details/129480220
来源地址:https://blog.csdn.net/qq_34532102/article/details/129480220