深圳融克迪特科技有限公司 Logo,金融科技,量化交易,软件开发

深圳融克迪特科技有限公司

RONG CREDIT TECHNOLOGY CO., LTD.

基础入门

【AkShare 系列 第7讲】排查空数据和字段报错

本讲把重点放在 AkShare 入门阶段最常见的排错场景,围绕输入参数、返回结构、类型转换和读写闭环四层检查顺序展开。目标是让初学者在遇到空数据、列名不一致或字段转型失败时,能先缩小问题范围,再做最小修改。

2026-04-23 智铨研究 阅读时长 9 分钟

目录

  1. 本节目标
  2. 为什么第 7 讲专门讲排错
  3. 选一段最容易出问题的代码来练
  4. 四层最小排错顺序
  5. 先查输入参数层
  6. 再查返回结构层
  7. 最后查类型转换层
  8. 读写闭环出问题时单独排
  9. 排错不是感觉更好了,而是问题范围更小了
  10. 空表时能先看输入参数
  11. 列名异常时能先打印结构
  12. 数值列异常时会单独做to_numeric
  13. 读写问题和接口问题能分开查
  14. 排错时最容易走偏的地方
  15. 一报错就先重装环境
  16. 看到字段不一致就直接改教程
  17. 把空值和失败混为一谈
  18. 一次同时改太多地方
  19. 什么叫“已经把问题查小了”
  20. 写一个最小诊断函数
  21. 把一个模糊问题压成一句明确判断
  22. 这一讲完成后你应该获得什么
  23. 先缩问题,再改代码
  24. 一个很实用的排错打印模板
  25. 空数据出现时可以优先核对的三件小事
  26. 字段问题和类型问题最好分开判断
  27. 为什么本讲对后面的独立练习特别重要
  28. 系列衔接
  29. 风险揭示与免责声明

1. 本节目标

本讲不再新增接口,而是回到前面几讲最容易让新手卡住的地方:空数据、字段不一致、列名找不到、类型转换失败。目标是建立一套很轻但实用的排查顺序,让你在 AkShare 返回不符合预期时,先判断问题属于哪一层,而不是一上来就怀疑整套环境坏了。

学完这一讲后,先把下面几件事做扎实:

  1. 区分“接口真的失败”和“数据只是长得和预期不一样”。
  2. 遇到空表时,先回看代码、日期、symbol 和字段,而不是盲目重装环境。
  3. 用几条最基本的检查语句快速定位问题落在参数、结构还是类型上。

这一步对入门者非常重要,因为后面真正决定你能不能独立往下走的,不是会不会多调一个接口,而是出问题时会不会排。

2. 为什么第 7 讲专门讲排错

前 1 到第 6 讲已经把最常见的动作都走了一遍:安装、拿列表、拿日线、拿指数、拿日历、保存 CSV。真正的现实是,这些动作不会每次都一帆风顺。你迟早会遇到:

  1. 表是空的。
  2. 列名和教程不一致。
  3. 某一列明明看着像数字,转型后全是空值。
  4. 保存完再读回时,日期列和原来不一样。

如果没有一套最小排错顺序,你就会不断在“重装环境”和“盲猜字段”之间打转。所以第 7 讲本质上是在帮你把前面六讲的练习路径稳住。

3. 选一段最容易出问题的代码来练

本讲不要求重新造新问题,直接拿前面已经写过的代码片段就可以。例如:

daily_df = ak.stock_zh_a_hist(
    symbol="000001",
    period="daily",
    start_date="20240101",
    end_date="20240215",
    adjust=""
)

或者:

preview_df = build_preview(daily_df, ["日期", "开盘", "收盘", "成交量"])

排错训练最重要的不是写出多复杂的代码,而是拿一段你以后真的会重复用的代码,学会给它做最小诊断。

4. 四层最小排错顺序

排错最怕同时动太多地方。下面四层顺序从最便宜的输入检查开始,一层层往下收窄。

5. 先查输入参数层

先看的是人最容易写错的地方:

print(symbol)
print(start_date, end_date)

很多问题其实和 AkShare 无关,只是:

这一步一定要先做,因为输入层最便宜、也最常错。

6. 再查返回结构层

拿到表后,先别急着转型,先看:

print(type(daily_df))
print(daily_df.shape)
print(daily_df.columns.tolist())

如果这里已经发现不是 DataFrame、列数是 0,或者关键列根本不在,那问题多半在“接口返回结果”和“你预期结构”之间,而不是 pandas 本身。

7. 最后查类型转换层

结构没问题以后,再看类型:

date_col = next(col for col in daily_df.columns if "日期" in str(col))
close_col = next(col for col in daily_df.columns if "收盘" in str(col))

parsed_date = pd.to_datetime(daily_df[date_col], errors="coerce")
parsed_close = pd.to_numeric(daily_df[close_col], errors="coerce")

print(parsed_date.isna().sum())
print(parsed_close.isna().sum())

如果这一步大量变成空值,说明问题不是“没返回表”,而是“这张表的字段虽然在,但内容形式和你预期不一致”。

8. 读写闭环出问题时单独排

如果问题出现在保存和回读阶段,就不要和接口问题混在一起排:

csv_df.to_csv("debug_sample.csv", index=False, encoding="utf-8-sig")
loaded_df = pd.read_csv("debug_sample.csv")
print(loaded_df.columns.tolist())
print(loaded_df.head())

这样能快速看出是文件没写出来、编码没对、还是回读后的列结构和原来发生了偏移。

9. 排错不是感觉更好了,而是问题范围更小了

本讲的成功标准,不是“所有报错都消失”,而是你已经能把问题收敛到更小范围。

10. 空表时能先看输入参数

assert isinstance(symbol, str)
assert len(symbol) > 0

11. 列名异常时能先打印结构

print(daily_df.columns.tolist())

12. 数值列异常时会单独做to_numeric

assert pd.to_numeric(daily_df[close_col], errors="coerce").notna().sum() >= 0

13. 读写问题和接口问题能分开查

只要你已经开始把“调用接口”和“读写文件”当成两层问题来拆,本讲就已经发挥作用了。

14. 排错时最容易走偏的地方

新手排错最容易急着大改环境或重写逻辑,真正更稳的做法,是先把问题拆小,再决定改哪里。

15. 一报错就先重装环境

这通常不是最优动作。除非你连导入包都失败,否则大多数问题更可能出在参数、列名或类型,而不是安装本身。

16. 看到字段不一致就直接改教程

先打印本地列名,再最小替换。不要一不一样就大改逻辑。排错的核心是缩小差异,不是把代码全部推翻重写。

17. 把空值和失败混为一谈

某些列有空值可能是正常业务现象,不等于接口彻底失效。你更应该看的是:关键字段是不是整体都不可用。

18. 一次同时改太多地方

如果你同时改 symbol、日期、接口名、列名和保存路径,就很难知道到底是哪一步修好了问题。第 7 讲强调的就是“小步定位”。

19. 什么叫“已经把问题查小了”

  1. 你已经知道是输入参数问题,而不是环境问题。
  2. 你已经知道是列名不匹配,而不是 DataFrame 根本没回来。
  3. 你已经知道是回读 CSV 后类型变化,而不是原始接口输出有问题。
  4. 你已经能说出“下一步该检查哪一层”,而不是整段代码全部重来。

这就已经是很有价值的排错进展。

20. 写一个最小诊断函数

import pandas as pd

def inspect_table(df: pd.DataFrame) -> None:
    print("type:", type(df))
    print("shape:", df.shape)
    print("columns:", df.columns.tolist())
    print(df.head())

inspect_table(daily_df)

这类函数看起来简单,但能显著降低你每次排错时的心智负担。排错不是靠灵感,而是靠反复执行一套稳定的小检查。

21. 把一个模糊问题压成一句明确判断

建议你做一个很短的作业:

  1. 选一段前面写过的 AkShare 代码。
  2. 假设它这次输出不符合预期。
  3. 先打印输入参数。
  4. 再打印 shapecolumns
  5. 最后写一句结论:问题更像出在哪一层。

如果你能做到这一点,第 7 讲就已经帮你建立了独立排错的起点。

22. 这一讲完成后你应该获得什么

如果你现在能说出这句话,第 7 讲就过关了:

“我遇到空数据或字段问题时,已经不会先乱改一大堆代码,而是会先按输入、结构、类型、读写四层顺序排查。”

这就是本讲真正的价值。

23. 先缩问题,再改代码

很多初学者在遇到问题时,会忍不住立刻开始改代码,而且一次改很多处。比如既改 symbol,又改日期,又改列名,还顺手把 to_csv()read_csv() 的路径一起重写。表面看像是在积极处理问题,实际上是在扩大问题面。第 7 讲更推荐的做法,是先把问题缩小成一句话,再决定要不要改代码。

例如,你可以先问自己:

  1. 我现在拿到的是不是一张空表?
  2. 如果不是空表,是不是列名和预期不同?
  3. 如果列名也对,是不是类型转换后变成了空值?
  4. 如果接口和类型都没问题,是不是文件读写出了偏差?

当问题已经被压缩到这四种之一时,你后面的修改就会小很多,也更容易验证是不是改对了。

24. 一个很实用的排错打印模板

如果你总觉得临场不知道该打印什么,可以直接保留下面这个模板:

def debug_snapshot(df):
    print('shape =', df.shape)
    print('columns =', df.columns.tolist())
    print('dtypes =')
    print(df.dtypes)
    print('head =')
    print(df.head(3))

debug_snapshot(daily_df)

这段代码不会替你自动修复问题,但它能非常稳定地把问题暴露到“人能直接看懂”的层面。对入门者来说,这往往比一上来追完整异常栈更有帮助,因为你先看见了结果长什么样,才更容易理解自己到底在修什么。

25. 空数据出现时可以优先核对的三件小事

空数据是这一讲最常见的场景之一。真正实用的做法,不是立刻把所有参数都推倒重来,而是先核对三个最便宜的小点:

  1. symbol 是否仍是你以为的那个代码。
  2. start_dateend_date 有没有写反或写得过窄。
  3. 当前接口是否真的支持你传入的这组参数。

很多时候,你会发现问题并不神秘,只是日期区间太窄、代码写错一位,或者沿用了上一讲别的对象的参数。越早把这些便宜错误排掉,越不会把时间浪费在错误方向上。

26. 字段问题和类型问题最好分开判断

新手还很容易把“没有这个字段”和“这个字段不能算”混成同一种错误。其实这两类问题差别很大:

  1. 没有字段,说明你先要回头看结构和列名。
  2. 字段在,但转型后几乎全空,说明你要看字段内容和类型。

例如:

if '收盘' not in daily_df.columns:
    print('先排列名问题')
else:
    close_series = pd.to_numeric(daily_df['收盘'], errors='coerce')
    print('有效数值个数 =', close_series.notna().sum())

这种拆法的好处是,你不会再把“列根本不在”和“列在但值不干净”当成同一个 bug 来处理。

27. 为什么本讲对后面的独立练习特别重要

AkShare 入门短课到第 7 讲,真正应该交给你的,不只是更多接口,而是一种碰到问题也能继续往前的能力。因为真实学习过程中,阻碍新手继续下去的,往往不是“不会再多写一行 API 调用”,而是“一报错就完全失去判断路径”。第 7 讲的作用,就是把这种失控感降低。你不需要一下子成为排错高手,只要先学会把问题拆成输入、结构、类型、读写四层,后面很多事就会从“完全不会”变成“有路径可走”。

28. 系列衔接

本讲是《AkShare数据获取入门短课》的第 7/8 讲,当前主题是《排查空数据和字段报错》。

上一讲:第 6 讲《保存行情到CSV》。

下一讲:第 8 讲《联动pandas算5日均线》。

29. 风险揭示与免责声明

风险揭示与免责声明

本页面内容仅用于量化研究与技术交流,旨在展示研究方法与流程,不构成对任何金融产品、证券或衍生品的要约、招揽、推荐或保证。

本文所涉历史数据、回测结果与示例参数不代表未来表现,也不应作为投资决策依据。

市场存在波动、流动性与执行偏差等不确定性,任何策略均可能出现收益波动或阶段性失效。

读者应结合自身风险承受能力进行独立判断,并在必要时咨询持牌专业机构意见。