KernelRanch
返回文章列表

requests 2.34 实用手册:连接复用、超时与四个真实报错

2026-11-10 #Python

用 Python 调一个 HTTP 接口,多数人第一反应是 requests。它把 URL 拼接、表单编码、Cookie、重定向、TLS 校验这些琐事收进一个 get() 里,脚本里写三行就能拿到 JSON。当需求变成"同时发几百个请求"或者"对接只支持 HTTP/2 的服务"时,它就不合适了:requests 是同步阻塞的,也没有 HTTP/2 支持,这两点得换 httpx 或 aiohttp。

安装与实际版本

python -m venv D:\ai\cache\temp\py-requests
D:\ai\cache\temp\py-requests\Scripts\python.exe -m pip install requests

安装到的是 2.34.2,依赖里的 urllib3 是 2.8.0:

Name: requests
Version: 2.34.2
Location: D:\ai\cache\temp\py-requests\Lib\site-packages
import requests

print(requests.__version__)
print(requests.packages.urllib3.__version__)
2.34.2
2.8.0

最小可运行示例

import requests

r = requests.get("https://example.com", timeout=10)
print("status:", r.status_code)
print("content-type:", r.headers["content-type"])
print("len(text):", len(r.text))
print("elapsed:", round(r.elapsed.total_seconds(), 3), "s")

本机跑出来的输出(走的是系统代理,所以 1.6 秒偏慢):

status: 200
content-type: text/html; charset=utf-8
len(text): 577
elapsed: 1.674 s

带上查询参数和 JSON 请求体也很直接,params 负责拼 query string,json= 负责序列化并设置 Content-Type:

r = requests.get("https://httpbin.org/get", params={"q": "kernelranch", "page": 2}, timeout=15)
print(r.url)
print(r.json()["args"])

r = requests.post("https://httpbin.org/post", json={"title": "笔记", "tags": ["python", "http"]}, timeout=15)
body = r.json()
print(body["headers"]["Content-Type"])
print(body["json"])
https://httpbin.org/get?q=kernelranch&page=2
{'page': '2', 'q': 'kernelranch'}
application/json
{'tags': ['python', 'http'], 'title': '笔记'}

注意 args 里的值都是字符串,page 传的是数字 2,服务端收到的还是 '2'。query string 本身没有类型。

常用 API 与参数

名称作用要点
requests.get/post/put/delete顶层便捷函数每次调用新建一个临时 Session,连接不复用
requests.Session()持久的会话对象复用 TCP 连接、保持 Cookie、可预置 headers 与 auth
timeout=超时单个数字同时作用于 connect 与 read;可传 (3.05, 27) 元组
params=拼 query string值为 None 的键会被丢掉
json=发 JSON 体自动 json.dumps 并设 application/json
data=发表单体字典会被表单编码;传字符串则原样发送
headers=请求头与 Session 级别的头合并,方法级的覆盖同名字段
r.raise_for_status()4xx/5xx 抛异常不会自动调用,需要自己写
r.json()解析响应体非 JSON 会抛 JSONDecodeError
stream=True + iter_content()流式下载不读完数据连接不会还回连接池

四个坑

第一个坑是不写 timeout。requests 默认没有超时,服务端不响应时进程会一直挂着,只写 requests.get(url) 的脚本在弱网下能卡几分钟。给个 2 秒超时打到 httpbin.org/delay/5,报错是:

ReadTimeout: ReadTimeout HTTPSConnectionPool(host='httpbin.org', port=443): Read timed out. (read timeout=2)

官方文档把 connect 超时和 read 超时分开解释,也提醒两者都不是墙钟时间:DNS 解析、多个地址依次重试都会让实际等待超过设定值。

第二个坑是每次调用顶层函数。requests.get() 每次都会新建连接、重新握手,在本地环回上都能测出差距。对着一个本地 ThreadingHTTPServer 发 30 次请求:

30 次 /json:Session 0.018s(0.58 ms/次)
30 次 /json:每次新建 0.352s(11.74 ms/次)

20 倍。跨机房时这个比例只会更大,因为握手要过 RTT。官方《Advanced Usage》里对 Session 的说明是它会"使用 urllib3 的连接池,对同一主机的多次请求复用底层 TCP 连接"。

第三个坑是以为状态码会自动报错。raise_for_status() 不存在"默认开启"这回事:

status_code: 503 ok: False
HTTPError: 503 Server Error: Service Unavailable for url: https://httpbin.org/status/503

r.ok 是 False,但代码不写 raise_for_status() 就接着往下走,后面 r.json() 才在别处炸掉,排查时线索就断了。

第四个坑是把 .json() 当万能解析。拿 HTML 页面调它,抛的是解析错误而不是编码错误:

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

同一类问题还有 URL 少写 scheme:

MissingSchema: Invalid URL 'example.com': No scheme supplied. Perhaps you meant https://example.com?

这两个报错都很短,看到 Expecting value: line 1 column 1 就该先怀疑响应根本不是 JSON,而不是去改编码。

小结

requests 的成本主要不在发请求,而在有没有复用连接。30 次本地请求,复用 0.58ms 一次,不复用 11.74ms 一次,差 20 倍;不写 timeout 则是另一个方向的代价,从"慢"变成"挂住"。这两件事写进团队模板比事后排查便宜得多。

剩下两个坑都关于错误处理的位置:状态码要显式 raise_for_status(),响应体要显式判断是不是 JSON。把这两步放在解析之前,报错会停在离原因最近的那一行。

数据来源与检索时间

检索时间 2026-11-10。版本号与运行输出为本机实测,接口语义依据官方文档。

来源URL对应数据
PyPI《requests》项目页https://pypi.org/project/requests/当前版本 2.34.2;Python 3.10+;Keep-Alive、连接池、超时、流式下载等特性清单
requests 官方文档《Advanced Usage》https://requests.readthedocs.io/en/latest/user/advanced/Session 复用 urllib3 连接池的说明;timeout 单值与 (connect, read) 元组语义;超时不是墙钟时间的提醒;proxies 会被环境代理覆盖的告警
本机运行记录D:\ai\cache\temp\py-requests2.34.2 / urllib3 2.8.0;example.com 与 httpbin.org 的响应;Session 与新建连接的 30 次对照;四段报错原文

核验说明

  • 虚拟环境 D:\ai\cache\temp\py-requests,Python 3.11.9,pip install requests 装到 2.34.2。
  • 真跑的示例:example.com 的 GET、httpbin.org 的 params 与 json= POST、503 的 raise_for_status()、/delay/5 配 2 秒超时、HTML 上调 .json()、裸域名触发 MissingSchema、本地 ThreadingHTTPServer 上 30 次 Session 与新建连接对照。
  • 本机开着系统代理(127.0.0.1:7898),example.com 的 1.674s 包含了代理转发;127.0.0.1 的请求被 Windows 的 ProxyOverride <local> 规则绕过,所以本地对照的数字是直连的。
  • 没测的部分:重试(urllib3.util.Retry 挂 HTTPAdapter)、stream=True 的流式下载、客户端证书、SOCKS 代理。这几项文档里给了写法,但没有在本机验证,文章里没有给结论。