Python接口调用实战_requests高级用法解析【教程】

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 接口重试三次,导致重复下单)。

  • 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 HTTPAdapter

session = 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./link/46b315dd44d174daf5617e22b3ac94ca", timeout=(3, 10))

Session 复用与 Cookie / Auth 自动管理

每次新建 requests.get() 都会创建新连接、丢弃 Cookie 和认证上下文;高频调用时既低效又容易被限流。用 requests.Session() 可复用 TCP 连接、自动维护 Set-Cookie、并支持预置 authheaders

  • 登录类接口务必用 Session:首次 post("/login") 后,后续请求自动携带服务端下发的 sessionid Cookie
  • 避免在 headers 里手动拼 "Authorization: Bearer xxx",改用 session.auth = ("user", "pass")session.headers.update({...})
  • Session 不是线程安全的,多线程场景下每个线程应持有独立 Session 实例

真正难的不是发请求,而是判断该不该重试、在哪一层校验状态、以及如何让失败表现得可追溯——这些细节往往藏在 response.headers、response.elapsed 和日志上下文里,而不是文档首页的那行 requests.get(url)