Python 请求很慢,需要很长时间才能完成 HTTP 或 HTTPS 请求
Python requests is slow and takes very long to complete HTTP or HTTPS request
当使用请求库请求网络资源或网站或网络服务时,请求需要很长时间才能完成。代码类似于以下内容:
import requests
requests.get("https://www.example.com/")
此请求需要超过 2 分钟(正好是 2 分 10 秒)才能完成!为什么这么慢,我该如何解决?
这个问题可以有多种可能的解决方案。 Whosebug 上有很多关于这些问题的答案,因此我会尝试将它们全部组合起来,以免您搜索它们的麻烦。
在我的搜索中,我发现了以下几层:
首先,尝试记录
对于许多问题,激活日志记录可以帮助您发现问题所在 (source):
import requests
import logging
import http.client
http.client.HTTPConnection.debuglevel = 1
# You must initialize logging, otherwise you'll not see debug output.
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
requests.get("https://www.example.com")
如果调试输出不能帮助您解决问题,请继续阅读。
如果您只需要检查服务器是否已启动,请尝试 HEAD 或流式请求
不请求所有数据,而只发送一个 HEAD 请求(source)可能会更快:
requests.head("https://www.example.com")
有些服务器不支持这个,那么你可以尝试流式传输响应(source):
requests.get("https://www.example.com", stream=True)
对于连续的多个请求,尝试使用 Session
如果您连续发送多个请求,您可以利用 requests.Session
加快请求速度。这确保与服务器的连接保持打开和配置,并且还保留 cookie 作为一个很好的好处。试试这个 (source):
import requests
session = requests.Session()
for _ in range(10):
session.get("https://www.example.com")
要并行化您的请求(尝试超过 10 个请求),请使用 requests-futures
如果您一次发送大量请求,每个请求都会阻止执行。您可以使用 requests-futures (idea from kederrac):
来并行化它
from concurrent.futures import as_completed
from requests_futures.sessions import FuturesSession
with FuturesSession() as session:
futures = [session.get("https://www.example.com") for _ in range(10)]
for future in as_completed(futures):
response = future.result()
注意不要同时用太多请求压垮服务器。
如果这也不能解决您的问题,请继续阅读...
原因可能不在于请求,而在于服务器或您的连接
在许多情况下,原因可能在于您请求的服务器。首先,通过以相同方式请求任何其他 URL 来验证这一点:
requests.get("https://www.google.com")
如果没有问题,您可以集中精力解决以下可能出现的问题:
服务器只允许特定的 user-agent 字符串
服务器可能会专门阻止 requests
,或者他们可能会利用白名单,或其他一些原因。要发送更好的 user-agent 字符串,试试这个 (source):
headers = {"User-Agent": "Mozilla/5.0 (X11; CrOS x86_64 12871.102.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.141 Safari/537.36"}
requests.get("https://www.example.com", headers=headers)
服务器rate-limits你
如果这个问题只是偶尔出现,例如几次请求后,服务器可能就是 rate-limiting 你。检查响应以查看它是否按照这些行读取内容(即“达到速率限制”、“工作 queue 超出深度”或类似内容;source)。
这里,解决方案只是在请求之间等待更长的时间,例如使用 time.sleep()
.
服务器响应格式不正确,导致解析问题
您可以通过不读取从服务器收到的响应来检查这一点。如果代码仍然很慢,这不是你的问题,但如果这解决了它,问题可能出在解析响应上。
要解决这些问题,请尝试:
r = requests.get("https://www.example.com")
r.raw.chunked = True # Fix issue 1
r.encoding = 'utf-8' # Fix issue 2
print(response.text)
IPv6 不工作,但 IPv4 可以
这可能是所有问题中最糟糕的。一个简单但奇怪的检查方法是添加一个 timeout
参数,如下所示:
requests.get("https://www.example.com/", timeout=5)
如果return是成功响应,问题应该出在 IPv6 上。原因是 requests
首先尝试 IPv6 连接。当超时时,它会尝试通过 IPv4 进行连接。通过将超时设置得较低,您可以强制它在更短的时间内切换到 IPv4。
通过使用 wget
或 curl
:
等进行验证
wget --inet6-only https://www.example.com -O - > /dev/null
# or
curl --ipv6 -v https://www.example.com
在这两种情况下,我们都强制工具通过 IPv6 连接以隔离问题。如果超时,请再次尝试强制 IPv4:
wget --inet4-only https://www.example.com -O - > /dev/null
# or
curl --ipv4 -v https://www.example.com
如果一切正常,你就找到问题了!但是你问怎么解决呢?
- 一个brute-force解决方案是disable IPv6完全。
- 您也可以 disable IPv6 for the current session 而已。
- 您可能只想force requests to use IPv4。 (在链接的答案中,对于 IPv4,您必须将代码调整为始终 return
socket.AF_INET
。)
- 如果你想为 SSH 修复这个问题,这里是 force IPv4 for SSH 的方法。 (简而言之,将
AddressFamily inet
添加到您的 SSH 配置。)
- 您可能还想检查问题是否出在您的 DNS or TCP 上。
当使用请求库请求网络资源或网站或网络服务时,请求需要很长时间才能完成。代码类似于以下内容:
import requests
requests.get("https://www.example.com/")
此请求需要超过 2 分钟(正好是 2 分 10 秒)才能完成!为什么这么慢,我该如何解决?
这个问题可以有多种可能的解决方案。 Whosebug 上有很多关于这些问题的答案,因此我会尝试将它们全部组合起来,以免您搜索它们的麻烦。
在我的搜索中,我发现了以下几层:
首先,尝试记录
对于许多问题,激活日志记录可以帮助您发现问题所在 (source):
import requests
import logging
import http.client
http.client.HTTPConnection.debuglevel = 1
# You must initialize logging, otherwise you'll not see debug output.
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
requests.get("https://www.example.com")
如果调试输出不能帮助您解决问题,请继续阅读。
如果您只需要检查服务器是否已启动,请尝试 HEAD 或流式请求
不请求所有数据,而只发送一个 HEAD 请求(source)可能会更快:
requests.head("https://www.example.com")
有些服务器不支持这个,那么你可以尝试流式传输响应(source):
requests.get("https://www.example.com", stream=True)
对于连续的多个请求,尝试使用 Session
如果您连续发送多个请求,您可以利用 requests.Session
加快请求速度。这确保与服务器的连接保持打开和配置,并且还保留 cookie 作为一个很好的好处。试试这个 (source):
import requests
session = requests.Session()
for _ in range(10):
session.get("https://www.example.com")
要并行化您的请求(尝试超过 10 个请求),请使用 requests-futures
如果您一次发送大量请求,每个请求都会阻止执行。您可以使用 requests-futures (idea from kederrac):
来并行化它from concurrent.futures import as_completed
from requests_futures.sessions import FuturesSession
with FuturesSession() as session:
futures = [session.get("https://www.example.com") for _ in range(10)]
for future in as_completed(futures):
response = future.result()
注意不要同时用太多请求压垮服务器。
如果这也不能解决您的问题,请继续阅读...
原因可能不在于请求,而在于服务器或您的连接
在许多情况下,原因可能在于您请求的服务器。首先,通过以相同方式请求任何其他 URL 来验证这一点:
requests.get("https://www.google.com")
如果没有问题,您可以集中精力解决以下可能出现的问题:
服务器只允许特定的 user-agent 字符串
服务器可能会专门阻止 requests
,或者他们可能会利用白名单,或其他一些原因。要发送更好的 user-agent 字符串,试试这个 (source):
headers = {"User-Agent": "Mozilla/5.0 (X11; CrOS x86_64 12871.102.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.141 Safari/537.36"}
requests.get("https://www.example.com", headers=headers)
服务器rate-limits你
如果这个问题只是偶尔出现,例如几次请求后,服务器可能就是 rate-limiting 你。检查响应以查看它是否按照这些行读取内容(即“达到速率限制”、“工作 queue 超出深度”或类似内容;source)。
这里,解决方案只是在请求之间等待更长的时间,例如使用 time.sleep()
.
服务器响应格式不正确,导致解析问题
您可以通过不读取从服务器收到的响应来检查这一点。如果代码仍然很慢,这不是你的问题,但如果这解决了它,问题可能出在解析响应上。
要解决这些问题,请尝试:
r = requests.get("https://www.example.com")
r.raw.chunked = True # Fix issue 1
r.encoding = 'utf-8' # Fix issue 2
print(response.text)
IPv6 不工作,但 IPv4 可以
这可能是所有问题中最糟糕的。一个简单但奇怪的检查方法是添加一个 timeout
参数,如下所示:
requests.get("https://www.example.com/", timeout=5)
如果return是成功响应,问题应该出在 IPv6 上。原因是 requests
首先尝试 IPv6 连接。当超时时,它会尝试通过 IPv4 进行连接。通过将超时设置得较低,您可以强制它在更短的时间内切换到 IPv4。
通过使用 wget
或 curl
:
wget --inet6-only https://www.example.com -O - > /dev/null
# or
curl --ipv6 -v https://www.example.com
在这两种情况下,我们都强制工具通过 IPv6 连接以隔离问题。如果超时,请再次尝试强制 IPv4:
wget --inet4-only https://www.example.com -O - > /dev/null
# or
curl --ipv4 -v https://www.example.com
如果一切正常,你就找到问题了!但是你问怎么解决呢?
- 一个brute-force解决方案是disable IPv6完全。
- 您也可以 disable IPv6 for the current session 而已。
- 您可能只想force requests to use IPv4。 (在链接的答案中,对于 IPv4,您必须将代码调整为始终 return
socket.AF_INET
。) - 如果你想为 SSH 修复这个问题,这里是 force IPv4 for SSH 的方法。 (简而言之,将
AddressFamily inet
添加到您的 SSH 配置。) - 您可能还想检查问题是否出在您的 DNS or TCP 上。