3. Python网络请求基础
在金融行情采集场景中,Python 与远端行情服务器之间的每一次“对话”,本质上都是一次 HTTP 请求与响应。理解这套通信机制,比记住某个 API 的调用方式更重要——因为行情接口的限流、鉴权、超时、断连,几乎都会以状态码、响应头或异常的形式暴露出来。本节围绕 requests 库,把 HTTP 协议、状态码、请求头、超时与重试这五个关键点串成一条可落地的采集链路。
一、核心原理:一次行情请求到底发生了什么
当执行 requests.get(url) 时,底层经历的是:建立 TCP 连接 → 发送 HTTP 请求报文(请求行、请求头、请求体)→ 服务端处理 → 返回响应报文(状态行、响应头、响应体)→ 连接复用或关闭。行情接口通常返回 JSON,因此响应体解析只是最后一步,真正决定成败的是前面的连接与协议层。
HTTP 请求头承担着“身份说明”和“内容协商”的职责。金融数据服务商常通过请求头识别调用方,例如 Authorization 携带 、User-Agent 标识客户端、Accept 声明期望的数据格式。缺少必要请求头,往往直接得到 401 或 403,而不是数据。
二、方法流程:从请求到可判定结果
一个稳健的行情请求函数,应当按固定顺序处理以下环节:
- 构造请求参数:URL、查询参数
params、请求头headers、超时timeout。 - 发送请求并捕获异常:区分连接超时、读取超时、DNS 失败、SSL 错误。
- 判定状态码:2xx 视为成功;4xx 多为请求方问题;5xx 与 429 属于可重试范畴。
- 解析响应体:优先
response.json(),并校验业务字段是否完整。 - 失败重试或降级:按退避策略重试,超过上限则记录日志并跳过。
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 次;并在日志中记录每次请求的状态码与耗时,便于后续定位。调整后任务连续运行数小时无中断。