Python将REST接口返回的多层嵌套JSON转换为细粒度DataFrame的方法
多层嵌套广告JSON转最细粒度扁平DataFrame的实现方法
你之前用pd.json_normalize达不到预期效果,核心问题是没有顺着数据的嵌套结构逐层拆解“动态字典key”的结构——这个方法本身完全可以覆盖你的需求,不需要额外找复杂的第三方库,按下面的步骤操作就能拿到逐行对应最小数据单元的扁平表。
适配你的数据结构的固定拆解流程
你的原始JSON结构是三层嵌套动态字典 + 数组类指标,拆解的核心逻辑是:从最外层开始,把所有“字典key代表ID”的结构,全部转换成“ID值作为独立列”的长表结构,最后在最细粒度层拆分数组类指标,拿到最高数据粒度的逐行数据。
1. 拆解最外层campaign层级
你最外层是result节点下,以campaign_id为key的字典结构,直接用pd.DataFrame.from_dict会把campaign_id当成列名,完全不符合长表要求,先手动转成带campaignID列的基础表:
import pandas as pd import json import requests # 你已经完成的请求逻辑 resp = requests.get("你的接口请求地址", headers={"自定义请求头"}) raw_data = json.loads(resp.text) # 提取result节点下的campaign级数据 campaign_root = raw_data["result"] # 转成带campaignID列的长表 campaign_base = pd.DataFrame([ {"campaignID": camp_id, **camp_attr} for camp_id, camp_attr in campaign_root.items() ])
2. 拆解campaign下嵌套的creative层级
每个campaign对应的属性里,又嵌套了以creative_id为key的字典,沿着campaign表逐行拆:
- 先把嵌套的creative字典列做explode拆成长形式
- 再把每个creative字典的key转成creativeID独立列,同时保留上一层的campaign所有属性
# 提取campaign下行内嵌套的creative映射关系 campaign_base["creative_map"] = campaign_base["creatives"] # 先把嵌套行拆平 creative_exploded = campaign_base.explode("creative_map") # 把creative_id从字典key转成独立列,展开creative的基础属性 creative_base = pd.concat([ creative_exploded.drop(columns=["creative_map", "creatives"]).reset_index(drop=True), pd.DataFrame([ {"creativeID": cre_id, **cre_attr} for cre_id, cre_attr in creative_exploded["creative_map"].items() ]) ], axis=1)
3. 拆解最细粒度的live统计层,含数组类指标
每个creative下嵌套的live_id为key的统计层,就是你要的最小粒度数据源,这一步用pd.json_normalize指定拆分数组的路径,直接拍平:
# 提取creative行内嵌套的live统计映射 creative_base["live_map"] = creative_base["live_stats"] live_exploded = creative_base.explode("live_map") # 先把live_id从字典key转成独立列 live_records = [ {"liveID": live_id, **live_attr} for live_id, live_attr in live_exploded["live_map"].items() ] # 用json_normalize拍平所有嵌套结构,拆分数组类指标到独立行 final_df = pd.json_normalize( data=live_records, # 填你结构里的数组类指标对应的key,比如分视频的统计数组名,没有数组的话传None record_path="video_metrics", # 指定要从上一层保留的所有列,包括你需要的campaignID、creativeID、device、liveID等 meta=["liveID", "device", "campaignID", "creativeID", "其他需要保留的上层统计字段"], errors="ignore" )
为什么之前的操作达不到效果
直接使用
pd.json_normalize失败的常见原因:
- 没有提前把“动态ID当字典key”的结构转成带ID列的记录形式,直接把最外层大字典整体传入,导致函数把动态ID当成顶层列名
- 没有给
record_path指定需要拆分成独立行的数组/嵌套层路径,导致所有嵌套结构被转成带点号的长列名,没有拆成逐行的最小粒度单元
零手写层级的懒人方案
如果不想逐层写拆解逻辑,可以用flatten_json工具递归展开所有嵌套字典:
from flatten_json import flatten # 对拆到live层的所有记录做递归展开 flattened_records = [flatten(record) for record in live_records] final_df = pd.DataFrame(flattened_records)
- 这个工具会自动把嵌套路径用下划线拼接成扁平列名,遇到数组会自动把数组索引追加在列名后
- 局限是不会自动把数组拆成独立行,展开后需要手动对数组类字段做
explode才能拿到最高粒度的逐行数据
内容的提问来源于stack exchange,提问作者JCBoysenBerry
相关产品推荐
相关产品推荐

