pd.json_normalize() 必须配合 record_path 和 meta 显式指定层级,否则仅展平顶层字段;遇嵌套列表如 "items": [...] 会整列塞入而非拆行;传入 json 字符串需先 json.loads();record_path 指向高基数列表时 meta 字段会被重复复制,应分步处理。

直接用 pd.json_normalize() 是最快路径,但必须配合 record_path 和 meta 显式指定层级,否则它只会展平一层,深层字段全丢。
为什么 pd.json_normalize(data) 常常“没反应”
因为默认行为只处理顶层字典的直系子字段,遇到 "items": [{"id":1,"name":"A"}] 这类嵌套列表,它会把整个列表塞进一列,而不是拆成多行。更常见的是传入 JSON 字符串(不是 Python dict),结果报错 TypeError: expected string or bytes-like object。
- 必须先
json.loads(),不能直接传字符串 - 如果数据是列表(如 API 返回的
[{...}, {...}]),直接传给json_normalize()即可 - 如果数据是字典且目标列表藏在
data["response"]["results"]里,就得用record_path=["response", "results"] -
max_level=2没用——它只影响字典展开深度,对列表内部的字典无效
如何正确提取“一对多”关系(如用户+多个订单)
目标是让每个订单占一行,并带上所属用户的 user_id、name 等字段。错误做法是直接 json_normalize(data, record_path="orders", meta=["user_id", "name"]) ——这要求所有用户都有 orders 字段,一旦某用户 orders 缺失或为空,整行就丢。
- 稳妥写法:先用
json_normalize(data, meta=["user_id", "name"], errors="ignore")提取主表,再单独处理orders列 - 对
orders列调用.explode(),再用json_normalize()展开每条订单 - 最后用
pd.concat(..., ignore_index=True)合并,避免索引错位 - 注意空列表:
.explode()遇到[]会生成NaN行,需用dropna(subset=["orders"])清洗
嵌套过深或结构不一致时怎么兜底
当 JSON 中同一字段有时是字典、有时是空值、有时干脆缺失(比如 "profile" 在部分记录里不存在),json_normalize() 会静默跳过或填 None,但列数可能不齐——后续 pd.DataFrame() 构造时报 ValueError: All arrays must be of the same length。
- 手动递归时,统一用
sep="_"拼键名(如"profile_city"),别用点号("profile.city"会和 pandas 的属性访问冲突) - 每一层都检查
isinstance(value, (dict, list)),非容器类型直接返回,避免TypeError - 对缺失键,显式赋
None而不是跳过,保证所有记录字段数一致 - 如果测量数组长度差异大(比如有的含 3 组
datax/datay,有的含 20 组),展平为宽表会导致大量稀疏列,此时更适合保留为列表列 + 后续用apply()处理
最易被忽略的一点:record_path 指向高基数列表(比如单个订单含 500 行商品)时,meta 字段会被复制 500 次——内存暴涨不是错觉,是设计使然。这时候分步处理不是“麻烦”,而是必须。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











