pandas.json_normalize() 专用于展平嵌套JSON,通过record_path提取记录列表、meta提升父级字段、sep自定义分隔符;需处理缺失字段、列表爆炸、性能优化及语义不一致等实际问题。

用 pandas.json_normalize() 展平嵌套字典
API 返回的 JSON 数据常含多层嵌套(比如 "user": {"profile": {"name": "Alice", "tags": ["dev", "python"]}}),直接传给 pd.DataFrame() 会把整层当字符串或 dict 存进单个单元格。必须先展平。
pandas.json_normalize() 是专为此设计的函数,它能递归提取嵌套字段,生成扁平列名(如 user.profile.name)。关键参数:
-
record_path:指定最深层的“记录列表”路径(如["data", "items"]),用于提取多条同构数据 -
meta:声明需提升到顶层的父级字段(如["id", "user.profile.name"]) -
sep:设置嵌套列名分隔符,默认.,可改用_避免点号在后续操作中引发语法歧义
示例:若 API 返回 {"status": "ok", "data": [{"id": 1, "user": {"name": "Bob", "addr": {"city": "Shanghai"}}}]},执行 pandas.json_normalize(data, record_path=["data"], meta=["status"], sep="_") 得到三列:id、user_name、user_addr_city、status。
遇到 KeyError 或缺失字段怎么办
真实 API 响应中,嵌套字段经常不全——某条记录缺 "user",另一条缺 "user.addr"。默认 json_normalize() 会报 KeyError 或填 None,但列结构可能错乱。
立即学习“Python免费学习笔记(深入)”;
安全做法是预设字段路径并容错:
- 用
errors="ignore"参数跳过无法解析的路径(慎用,可能掩盖结构问题) - 更可靠的是先统一补全缺失层级:对原始响应列表,用
dict.setdefault()逐层初始化空字典,再传入json_normalize() - 若字段类型混杂(如有时是
dict,有时是None),展平后该列 dtype 会变成object,后续用pd.Series.map()或.str.get()提取子字段时需加na_action="ignore"
处理含列表的嵌套字段(如 "tags": ["a", "b"])
json_normalize() 默认把列表原样塞进单元格,不会自动展开成多行。想“爆炸”成行(即一对多展开),得手动处理。
典型流程:
- 先用
json_normalize()得到含列表的 DataFrame - 对目标列(如
tags)调用explode()方法:df.explode("tags"),每项单独一行 - 若列表项本身是字典(如
[{"id": 1}, {"id": 2}]),再对新列用json_normalize()单独展平,或用pd.json_normalize(df["tags"].dropna())提取后merge回主表
注意:explode() 会复制其他列值,若原数据量大且列表很长,内存占用激增——先确认是否真需要行展开,还是用 str.join() 或 apply(list) 保留为聚合形式更合适。
性能差?别在循环里反复调用 json_normalize()
有人习惯对每个 API 分页响应单独 json_normalize() 再 pd.concat(),这在响应结构一致时可行,但效率低:每次解析都重建列映射,且 concat 多次触发内存拷贝。
正确姿势是:
- 收集全部原始响应(列表 of dict),一次性传入
json_normalize() - 若各页结构微异(如某些页多一个字段),先用
pd.json_normalize([{}])空跑一次获取完整列集,再用reindex(columns=full_cols)对齐 - 极端情况(超深嵌套或动态 key),考虑用
jsonpath-ng提前提取关键路径,再构造干净 dict 列表喂给pd.DataFrame()
真正麻烦的不是嵌套深度,而是字段语义不稳定——比如 "metadata" 有时是字典,有时是字符串,有时为空。这种必须在 json_normalize() 前做清洗,靠参数解决不了。


















