限频与重试:与数据源"礼貌相处"的工程学
我的掌握程度:
先掌握这个答案
限速三实现:①固定sleep:每请求后time.sleep(0.5)——简单够用(个人研究默认);②令牌桶:限制每秒最多N个请求(并发场景)——进阶;③库内置:ccxt的enableRateLimit=True——能用就用。指数退避重试:失败后sleep 1s、2s、4s…重试(通常3次)——道理:故障多为暂时性(服务器忙/限频窗口),等待让时间治愈;退避「指数」是因为连续失败说明问题在升级,线性等待不够。三条底线:①读对方文档的rate limit并遵守(写了就遵守——契约);②避开高峰(对方服务器也有作息);③识别「429太多请求」与「403被封」的区别(前者等、后者停+换策略)。
先读结论,再核对下方使用边界;数字例题为教学情景,涉及实际行情与规则时请核对原始来源。
从理解到使用
本阶段实战方法 · 一条能复现的数据流水线
把接口返回的数据变成可用于因子研究的面板时,先处理时点、主键与单位。
- 建立数据字典:字段、单位、时区、交易日历、数据源、抓取时间、原始价与复权因子。
- 用(date, code)查重,区分停牌、未上市、窗口不足和真实缺失;不要用向后填充把未来值带回过去。
- 保存原始快照与清洗日志,固定随机种子和环境版本;接口字段变化时先核对官方文档。
使用边界
- 报告期不等于公告可用日,盘后公告通常不能用于当天收盘前决策。
- API输出名为“动态市盈率”时不能直接改名TTM;浏览器练习不支持任意原生库或行情抓取接口。
不懂的术语:指数(Index)
背景 · 从一个困惑说起
下载战役中你一定会遇到它:请求太快被封、偶发超时、接口偶发抽风。限频与重试不是可选的礼貌,是与一切数据源(免费或付费)长期共处的生存技能。
由此产生的问题
限速的三种实现(固定sleep/令牌桶/库内置)?指数退避重试的写法与道理?「礼貌爬虫」的三条底线?
核心概念
限速三实现:①固定sleep:每请求后time.sleep(0.5)——简单够用(个人研究默认);②令牌桶:限制每秒最多N个请求(并发场景)——进阶;③库内置:ccxt的enableRateLimit=True——能用就用。指数退避重试:失败后sleep 1s、2s、4s…重试(通常3次)——道理:故障多为暂时性(服务器忙/限频窗口),等待让时间治愈;退避「指数」是因为连续失败说明问题在升级,线性等待不够。三条底线:①读对方文档的rate limit并遵守(写了就遵守——契约);②避开高峰(对方服务器也有作息);③识别「429太多请求」与「403被封」的区别(前者等、后者停+换策略)。
为什么需要它
①工程自我修养:会写重试的研究者 vs 不会的——前者一周稳定拿到数据,后者每周重下;②对免费资源的伦理:akshare们抓取的源站承担着成本——礼貌使用是可持续的前提(也是不让自己IP进黑名单的私利);③错误分类意识:可重试错误(超时/5xx/429)与不可重试错误(404/参数错)分开处理——盲目重试后者是浪费。
实际应用场景
标准模板:try→except分类→可重试则退避→3次仍败进失败清单——q-328模板的异常分支展开;把这套逻辑封装成装饰器(@retry)复用到所有接口。
与之前知识的联系
q-321(safe_fetch雏形)与q-328(战役)的重试内核;q-060(字段校验)是不出错时的下游;错误分类的思想通向一切系统的健壮性设计。
⚠ 常见误解
- 重试就是再跑一次:无退避的立即重试会加剧限频——等待是重试的一部分
- 被封了换IP就好:这是对抗而非共处——遵守文档限速才是正路(也最省事)
- 所有异常都该重试:参数错误重试一万次也不会对——先分类再行动(可重试/需修改/需放弃)
🛠 本题实践任务
把retry装饰器加到你的下载脚本上;故意把sleep调小触发一次限频,观察退避日志的行为(没有触发就人为把max等待调小模拟)。记录一次处置日志。
💡 提示(先自己想5分钟再看)
装饰器让所有接口共享重试逻辑——阶段4「函数复用」理念的标准应用。
✅ 验收标准
带退避日志的下载脚本。你的数据管道有了自动愈合能力。
在你的浏览器内运行。首次需联网下载 Python;支持标准库和 Pyodide 提供的 NumPy、pandas 等包。行情抓取、系统文件访问和部分第三方库请在本地环境运行。输出自检仅比对结果关键词,不代替逻辑审查。
Python · 独立工作线程 · 执行限时10秒
🤖 AI导师
导师会先检查你的前置知识(批量下载、异常处理),再用案例、公式和代码帮助理解。此功能需要先启用服务。