🕵️ Python requests 中 timeout 的坑:连接超时与读取超时的那些事
最近在使用 Python 的 requests
库抓取数据时,遇到一些很“玄学”的超时错误。明明设置了合理的 timeout
,请求依然抛出 Read timed out
,而且有时是在连接阶段就失败了。这让我决定做一次系统的实验,深入理解 requests
的 timeout 参数到底怎么回事。
⏱ timeout 参数到底指什么?
requests.get(url, timeout=(连接超时, 读取超时))
timeout 参数有两种写法:
timeout = 5
:连接和读取加起来 5 秒,一刀切;timeout = (5, 10)
:连接最多等 5 秒,连接成功后读取最多等 10 秒。
所以:
requests.get("https://example.com", timeout=(3, 10))
意思是:最多等 3 秒连上服务器,连上之后最多再等 10 秒读取数据。
🧪 实验:谁在超时?
我选择了一个会延迟 8 秒才返回的地址:
https://httpbin.org/delay/8
然后用如下代码跑了一组测试:
import requests
def debug_timeout(timeout):
try:
requests.get("https://httpbin.org/delay/8", timeout=timeout)
except Exception as e:
print(f"[DEBUG] Caught exception: {e}")
test_examples = [
(0.8, 0.1),
(0.8, 0.2),
(0.9, 0.1),
(0.9, 0.2),
(1.0, 0.1),
(1.0, 0.2),
(1.1, 0.1),
(1.1, 0.2),
(1.2, 0.1),
]
for timeout in test_examples:
debug_timeout(timeout)
输出如下:
[DEBUG] Caught exception: HTTPSConnectionPool(host='httpbin.org', port=443): Read timed out. (read timeout=0.1)
[DEBUG] Caught exception: HTTPSConnectionPool(host='httpbin.org', port=443): Read timed out. (read timeout=0.2)
...
观察发现:
- 哪怕是
(0.8, 0.1)
这种,连接阶段超时,报错也是Read timed out
- 明明连接都没成功,异常提示却是
read timeout
❗ 为什么连接超时也显示成“read timeout”?
这是 requests
的坑。
它底层用的是
urllib3
,而 urllib3 会把某些连接错误和 TLS 握手失败统一包装成ReadTimeoutError
。
也就是说:
- 连接失败、握手失败、SSL 慢,有可能都显示为
Read timed out
; - 你以为是服务器响应慢,其实是你根本连不上。
✅ 如何精确判断是哪种超时?
推荐你这样写:
from requests.exceptions import ConnectTimeout, ReadTimeout
try:
requests.get("https://httpbin.org/delay/8", timeout=(1, 0.5))
except ConnectTimeout:
print("连接超时")
except ReadTimeout:
print("读取超时")
except Exception as e:
print("其他错误:", e)
用异常类去捕捉,比看字符串强一万倍。
🧠 小结
类型 | 含义 | 注意事项 |
---|---|---|
connect 超时 | TCP 建立连接超时 | 域名解析失败、网络慢、TLS 握手慢也算在内 |
read 超时 | 已连接,等待服务器响应超时 | 常见于服务器处理太慢 |
单个 timeout | 表示连接+读取总时间限制 | 不推荐,会混淆问题原因 |
🚀 建议实践
- 扫描代理/测速时,推荐 timeout=(1, 1) 或 (1, 0.5);
- 实时接口可设为
(2, 5)
或(3, 5)
; - 做异常处理要区分
ConnectTimeout
和ReadTimeout
; - 不建议用
timeout=5
这种形式,不好定位问题;
🔚 最后的话
这次实验让我意识到,“read timeout” 不一定真的是“读取超时”,有时候你连连接都没成功。
建议大家做网络爬虫、代理测速或接口监控时,不要只看报错信息的表面,要结合 timeout 参数逻辑和实际场景去理解。