当使用 requests 库请求 Web 资源或网站或 Web 服务时,请求需要很长时间才能完成。代码类似于以下内容:
import requests
requests.get("https://www.example.com/")
此请求需要超过 2 分钟(正好 2 分 10 秒)才能完成!为什么这么慢,我该如何解决它?
这个问题可以有多种可能的解决方案。对于这些问题,StackOverflow 上都有很多答案,所以我会尝试将它们全部结合起来,以节省您搜索它们的麻烦。
在我的搜索中,我发现了以下几层:
对于许多问题,激活日志记录可以帮助您发现问题所在(来源):
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 请求会更快(source):
requests.head("https://www.example.com")
有些服务器不支持此功能,那么您可以尝试流式传输响应(source):
requests.get("https://www.example.com", stream=True)
如果您连续发送多个请求,您可以使用
requests.Session
来加快请求速度。这可以确保与服务器的连接保持打开和配置状态,并且还可以保留 cookie,这是一个很好的好处。试试这个(来源):
import requests
session = requests.Session()
for _ in range(10):
session.get("https://www.example.com")
如果一次发送大量请求,每个请求都会阻塞执行。您可以利用,例如,requests-futures(来自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")
如果效果良好,您可以集中精力解决以下可能的问题:
服务器可能会专门阻止
requests
,或者他们可能会利用白名单或其他原因。要发送更好的用户代理字符串,请尝试此(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)
如果这个问题只是偶尔出现,例如在几次请求之后,服务器可能会对您进行速率限制。检查响应,看看它是否读取了类似的内容(即“达到速率限制”、“超出工作队列深度”或类似内容;源)。
这里,解决方案就是在请求之间等待更长的时间,例如使用
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)
这可能是所有问题中最糟糕的。一种简单但奇怪的检查方法是添加一个
timeout
参数,如下所示:
requests.get("https://www.example.com/", timeout=5)
如果返回“成功响应”,则问题应该出在 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
如果这工作正常,那么您已经找到问题了!但你问如何解决?
强力解决方案是
socket.AF_INET
对于 IPv4。)如果您想解决 SSH 的此问题,请参阅以下方法AddressFamily inet
添加到您的 SSH 配置中。)您可能还想检查问题是否出在您的
与具有浏览器请求标头的所有外观的服务器相比,像 AWS 这样的服务器通常需要 5x 多的时间来解析没有标头的请求(您可以使用此示例)-
HEADERS = {
"Accept": "application/json, text/plain, */*", // change this to your need
"Accept-Encoding": "gzip, deflate, br, zstd", // this as well
"Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8", // can be ignored
"Connection": "keep-alive", // usually kept
"Host": "your-target.com",
"Origin": "your-target.com", // use this if there is some security issue
"Referer": "", // can be ignored
"Sec-Fetch-Dest": "empty", // security add-on, can be ignored
"Sec-Fetch-Mode": "cors", // security add-on, can be ignored
"Sec-Fetch-Site":"same-site", // security add-on, can be ignored
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", // absolute must (recommended)
}