Skip to content

fix:将Akshare的多次调用转换为一次调用,一次获取整个A股行情,一次获取整个ETF行情,减少被反爬虫的几率#123

Closed
Wu-Gao wants to merge 0 commit intoZhuLinsen:mainfrom
Wu-Gao:main
Closed

fix:将Akshare的多次调用转换为一次调用,一次获取整个A股行情,一次获取整个ETF行情,减少被反爬虫的几率#123
Wu-Gao wants to merge 0 commit intoZhuLinsen:mainfrom
Wu-Gao:main

Conversation

@Wu-Gao
Copy link
Copy Markdown
Contributor

@Wu-Gao Wu-Gao commented Jan 23, 2026

变更类型

  • [✅] 🐛 Bug 修复
  • ✨ 新功能
  • 📝 文档更新
  • 🎨 代码优化/重构
  • ⚡ 性能优化
  • 🔧 配置/构建相关

变更描述

将Akshare的多次调用转换为一次调用,之前的Akshare每个自选股都要调用一次,频率过高容易被反爬虫,现在修改为一次获取整个A股行情,一次获取整个ETF行情,减少被反爬虫的几率

关联 Issue

关联的 Issue 编号(如有):fixes #108 #109 #119

测试说明

描述如何测试这些变更:

  1. 一次获取整个A股行情,一次获取整个ETF行情
  2. 获取存在缓存里后,后续分析直接拉取缓存的数据

检查清单

  • [✅] 代码符合项目规范
  • [✅] 已添加必要的注释/文档
  • [✅] 已在本地测试通过
  • 已更新相关文档(如需要)

截图(如适用)

如有 UI 变更,请附上截图。

其他说明

其他需要说明的内容。

@github-actions
Copy link
Copy Markdown

🤖 自动审查报告

项目 结果
📊 变更文件 4 个
➕ 新增行数 388 行
➖ 删除行数 13 行
🔍 静态检查 ✅ 通过
🧠 AI 审查 ✅ 已完成

📁 修改的文件

  • 📝 data_provider/akshare_fetcher.py (+274/-3)
  • 📝 data_provider/efinance_fetcher.py (+5/-5)
  • 📝 main.py (+63/-4)
  • 📝 storage.py (+46/-1)

🧠 AI 代码审查意见

作为资深 Python 代码审查专家,我对此次代码变更进行了详细审查。

总体评价

建议合入

这是一项非常出色的改进,通过引入批量预取和缓存机制,显著优化了实时行情数据的获取效率,减少了对 Akshare API 的调用次数,并提升了系统的健壮性。代码设计合理,考虑周全。

详细审查意见

1. 🔒 安全性

  • 无明显安全漏洞。 变更主要集中在数据获取和缓存逻辑,没有引入用户输入直接影响系统操作或敏感信息泄露的风险。AkshareFetcher 依然保留了随机 User-Agent 和限速策略,对外部 API 的访问依然是受控的。

2. 🐛 潜在 Bug

  • Akshare 列名依赖性:
    • 问题: _parse_stock_quote_from_df_parse_etf_quote_from_df 方法中,解析 DataFrame 时直接使用了 Akshare 返回的原始列名(如 '代码', '名称', '最新价', '涨跌幅' 等)。如果 Akshare 未来更新其 API 接口,改变了这些列名,会导致解析失败。
    • 建议: 虽然 Akshare 的列名通常比较稳定,但这是一个外部依赖的潜在风险点。可以在 _parse_*_quote_from_df 方法中增加更详细的错误日志,或者在代码中明确标注这些列名的来源和潜在风险。目前这种处理方式是合理的,因为它与 Akshare 紧密集成。
  • ETF 数据字段缺失:
    • 问题:_parse_etf_quote_from_df 中,pe_ratio, pb_ratio, change_60d 被硬编码为 0.0
    • 说明: 这很可能是因为 Akshare 的 fund_etf_spot_em 接口不提供这些字段。这不是一个 bug,而是数据源的限制。建议在代码注释中明确说明此情况,以防后续维护者误解。
  • main.pyprefetch_all_realtime_quotes的调用:
    • 问题: main.py 的 diff 仅展示了 prefetch_all_realtime_quotes 方法的定义,但未显示其在何处被调用。
    • 说明: 为了让批量预取功能生效,该方法必须在 StockAnalysisPipelinerun 或其他初始化/执行开始阶段被调用。假设在完整的 main.py 中,它已被正确地在程序启动时调用。

3. ⚡ 性能

  • 显著提升: 这是此次变更的核心亮点。
    • 通过一次性批量获取所有 A 股和 ETF 的实时行情,将原来 N 次(N 为股票数量)的 API 调用减少到仅 2 次(一次 A 股,一次 ETF)。
    • 引入的 _prefetch_cache 机制,配合 10 分钟的 ttl,有效避免了在短时间内重复调用 API,大大提高了后续获取单个股票行情的速度。
    • 确保了所有股票分析都基于同一时间点的数据快照,提升了数据一致性。
  • 合理降级: 缓存过期、数据为空或预取失败时,系统能够优雅地降级到逐个获取模式,保证了功能的可用性,即使在预取不成功的情况下也能正常运行。

4. 📖 可读性

  • 良好:
    • 新增的 _prefetch_cache 字典结构清晰,ttl 明确。
    • 新增方法(如 prefetch_all_stock_quotes, prefetch_all_etf_quotes, get_quote_from_prefetch)命名准确,符合其功能。
    • 代码中增加了详细的日志信息,包括预取状态、股票数量、耗时等,方便调试和监控。
    • safe_float 辅助函数处理了潜在的数据类型转换错误和 pd.isna 情况,提高了健壮性。
    • main.py 中对预取机制的日志说明("已启用批量预取架构")增强了系统的透明度。
  • 代码注释: 关键方法和变量都有清晰的 Docstring 或行内注释,解释了功能、参数和返回值。

5. 🏗️ 架构设计

  • 符合项目架构: 变更符合现有数据提供者(Fetcher)的职责划分。AkshareFetcher 负责 Akshare 相关的数据获取,并在其内部管理缓存,而 main.py 作为调度器,负责调用 Fetcher 的高级接口并处理结果。
  • 职责分离: AkshareFetcher 内部区分了批量预取和从预取数据中解析单只股票的方法 (_parse_stock_quote_from_df, _parse_etf_quote_from_df),职责清晰。
  • 高内聚低耦合: 预取逻辑和缓存管理被封装在 AkshareFetcher 内部,外部调用者(如 main.py)只需通过统一接口 (get_quote_from_prefetch) 获取数据,无需关心底层缓存细节,实现了较好的模块化。
  • 可扩展性: 如果未来需要支持其他数据源的批量预取,可以参照此模式在对应的 Fetcher 中实现。


💡 提示: 请确保代码已通过本地测试,并遵循项目代码规范。

@touhoufan2024
Copy link
Copy Markdown

感谢付出

@ZhuLinsen
Copy link
Copy Markdown
Owner

Hi, PR #113 (Bot framework) has just been merged.

Your current implementation of prefetch_all_realtime_quotes runs unconditionally, which means even a single stock query via the new bot would trigger a download of the entire market data (5000+ stocks). This will cause significant latency for bot users.

Please rebase on main and modify the logic in main.py so that prefetching is only enabled when running in batch mode (e.g., when stocks list is empty or large).

Thanks for the optimization!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 希望优化获取大盘数据的行为

3 participants