requests 默认不重试、无超时、4xx不抛异常是接口调用失败主因;必须显式设 timeout=(connect, read),检查 status_code 或用 raise_for_status(),GET 可安全重试而 POST 需幂等,Session 复用连接并管理 Cookie/Auth。

requests 默认不自动重试,超时默认是永久等待,4xx 错误不会抛异常——这三点是绝大多数接口调用失败却查不出原因的根源。
requests.get() 和 requests.post() 的 timeout 参数必须显式设置
不设 timeout 会导致请求卡死在 DNS 解析、连接建立或响应读取任一环节,尤其在容器或云函数中会直接触发超时熔断。
-
timeout是个二元组:(connect_timeout, read_timeout),例如timeout=(3, 10)表示连接最多等 3 秒,连上后读响应最多等 10 秒 - 只传单个数字(如
timeout=5)等价于timeout=(5, 5),但生产环境建议拆开控制 - 若服务端响应慢但稳定(如报表导出),可适当提高
read_timeout,但connect_timeout建议保持 ≤3 秒
4xx/5xx 响应默认不抛异常,需手动检查 response.status_code
requests 把 HTTP 状态码 ≥400 视为“合法响应”,response.raise_for_status() 才会触发 HTTPError。很多同学写了 r = requests.post(...) 却没检查状态码,结果拿到 401 Unauthorized 还以为数据正常。
- 推荐写法:始终检查
r.status_code == 200或使用r.raise_for_status() -
raise_for_status()对 4xx/5xx 统一抛requests.exceptions.HTTPError,可统一捕获 - 注意:即使状态码是 200,业务也可能返回
{"code": 500, "msg": "库存不足"},这类需额外解析r.json().get("code")
如何安全实现带退避的自动重试
requests 本身不带重试逻辑,靠 urllib3.Retry 配合 requests.adapters.HTTPAdapter 实现。盲目全局启用重试可能放大雪崩风险(比如对 POST 接口重试三次,导致重复下单)。
立即学习“Python免费学习笔记(深入)”;
- GET 请求可安全重试;POST/PUT/DELETE 必须确认服务端是否幂等,否则禁用重试
- 推荐配置:最大重试 2 次、指数退避(1s → 2s)、仅对连接错误和 5xx 重试,跳过 4xx
- 示例代码中
status_forcelist=(500, 502, 503, 504)明确限定重试范围
import requests from urllib3.util.retry import Retry from requests.adapters import HTTPAdaptersession = requests.Session() retry_strategy = Retry( total=2, backoff_factor=1, status_forcelist=(500, 502, 503, 504), allowed_methods=["HEAD", "GET", "OPTIONS"] ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter)
后续所有 session.get() / session.post() 都自动带重试
r = session.get("https://www.php.cn/link/46b315dd44d174daf5617e22b3ac94ca", timeout=(3, 10))
Session 复用与 Cookie / Auth 自动管理
每次新建 requests.get() 都会创建新连接、丢弃 Cookie 和认证上下文;高频调用时既低效又容易被限流。用 requests.Session() 可复用 TCP 连接、自动维护 Set-Cookie、并支持预置 auth 或 headers。
- 登录类接口务必用 Session:首次
post("/login")后,后续请求自动携带服务端下发的sessionidCookie - 避免在 headers 里手动拼
"Authorization: Bearer xxx",改用session.auth = ("user", "pass")或session.headers.update({...}) - Session 不是线程安全的,多线程场景下每个线程应持有独立 Session 实例
真正难的不是发请求,而是判断该不该重试、在哪一层校验状态、以及如何让失败表现得可追溯——这些细节往往藏在 response.headers、response.elapsed 和日志上下文里,而不是文档首页的那行 requests.get(url)。










