3. Python网络请求基础

在金融行情采集场景中,Python 与远端行情服务器之间的每一次“对话”,本质上都是一次 HTTP 请求与响应。理解这套通信机制,比记住某个 API 的调用方式更重要——因为行情接口的限流、鉴权、超时、断连,几乎都会以状态码、响应头或异常的形式暴露出来。本节围绕 requests 库,把 HTTP 协议、状态码、请求头、超时与重试这五个关键点串成一条可落地的采集链路。

一、核心原理:一次行情请求到底发生了什么

当执行 requests.get(url) 时,底层经历的是:建立 TCP 连接 → 发送 HTTP 请求报文(请求行、请求头、请求体)→ 服务端处理 → 返回响应报文(状态行、响应头、响应体)→ 连接复用或关闭。行情接口通常返回 JSON,因此响应体解析只是最后一步,真正决定成败的是前面的连接与协议层。

HTTP 请求头承担着“身份说明”和“内容协商”的职责。金融数据服务商常通过请求头识别调用方,例如 Authorization 携带 、User-Agent 标识客户端、Accept 声明期望的数据格式。缺少必要请求头,往往直接得到 401 或 403,而不是数据。

行情采集请求:协议层、状态层与重试层 客户端 requests URL + 方法 GET 请求头 Headers 超时 timeout 重试 Retry 连接池 Session HTTP 协议层 请求行 / 请求头 响应状态行 响应头 / 响应体 Keep-Alive 复用 TLS 加密传输 行情服务端 鉴权校验 限流 / 配额 返回 JSON 行情 状态码 200/429/5xx 响应时间波动 请求 转发 响应 解析/重试

二、方法流程:从请求到可判定结果

一个稳健的行情请求函数,应当按固定顺序处理以下环节:

  1. 构造请求参数:URL、查询参数 params、请求头 headers、超时 timeout。
  2. 发送请求并捕获异常:区分连接超时、读取超时、DNS 失败、SSL 错误。
  3. 判定状态码:2xx 视为成功;4xx 多为请求方问题;5xx 与 429 属于可重试范畴。
  4. 解析响应体:优先 response.json(),并校验业务字段是否完整。
  5. 失败重试或降级:按退避策略重试,超过上限则记录日志并跳过。
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
retry = Retry(
    total=3,
    backoff_factor=0.5,
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods=["GET"]
)
session.mount("https://", HTTPAdapter(max_retries=retry))

headers = {
    "User-Agent": "Mozilla/5.0 (compatible; QuoteCollector/1.0)",
    "Authorization": "Bearer YOUR_",
    "Accept": "application/json"
}

try:
    resp = session.get(
        "https://api.example.com/v1/quote",
        params={"symbol": "600519", "interval": "1m"},
        headers=headers,
        timeout=(3.05, 10)   # (连接超时, 读取超时)
    )
    resp.raise_for_status()
    data = resp.json()
except requests.exceptions.Timeout:
    print("请求超时,进入重试或降级")
except requests.exceptions.HTTPError as e:
    print("HTTP错误:", e.response.status_code)
except requests.exceptions.RequestException as e:
    print("网络异常:", e)

三、状态码与请求头:读懂服务端的“回话”

状态码是服务端对请求最直接的判定。行情采集中最常见的几类如下表所示,理解它们能帮助你决定“重试、修正参数还是直接放弃”。

状态码含义行情场景典型原因建议动作
200成功正常返回行情 JSON解析并入库
301/302重定向接口域名迁移跟随重定向或更新 URL
400请求错误symbol 参数格式错误修正参数,不重试
401未授权 缺失或过期刷新凭证后重试
403禁止访问IP 未在白名单检查权限,不盲目重试
429请求过多触发限流配额退避后重试,降低频率
500/502/503服务端异常行情源抖动或维护指数退避重试
请求头小技巧:把 Authorization、User-Agent、Accept 统一封装进一个字典,避免每次调用重复书写; 建议从环境变量读取,不要硬编码在源码中。

四、超时与重试:把“不确定”变成“可控”

超时是采集稳定性的第一道防线。若不设置 timeout,请求可能无限等待,导致整个采集任务卡死。推荐使用元组形式分别控制连接超时与读取超时,例如 timeout=(3.05, 10):前者防止连不上,后者防止连上后迟迟不返回数据。

重试不是“无脑重发”。合理的重试策略应满足三点:只对可恢复错误重试(429、5xx、超时);采用指数退避避免加剧服务端压力;设置最大次数与总耗时上限。对于 400、401、403 这类请求方错误,重试通常没有意义,反而浪费配额。

常见误区:把 raise_for_status() 当成万能校验。它只检查 HTTP 状态码,若服务端返回 200 但业务体是 {"code": 1001, "msg": "invalid "},仍需自行校验业务字段。行情采集中“HTTP 成功但业务失败”非常常见。

五、案例复盘:一次限流引发的采集中断

某次分钟级行情采集任务在运行 20 分钟后大量报错。日志显示状态码从 200 逐渐变为 429,随后出现 503。复盘发现:脚本未设置重试退避,遇到 429 后立即重发,进一步触发限流;同时未复用 Session,每次请求都新建连接,放大了服务端压力。

修正方案是:使用 Session 复用连接;为 429 与 5xx 配置指数退避重试;将采集频率从每秒 5 次降至每秒 1 次;并在日志中记录每次请求的状态码与耗时,便于后续定位。调整后任务连续运行数小时无中断。

本节要点回顾:requests 负责发起请求,HTTP 协议决定通信格式,状态码决定后续动作,请求头决定身份与内容协商,超时与重试决定系统在异常下的韧性。把这五点组合成统一的请求封装,是后续增量同步与断点续传的基础。