查看: 55|回复: 3

Python读取JSON转Excel:嵌套展开与批量转换脚本

[复制链接]
发表于 23 分钟前 | 显示全部楼层 |阅读模式
接口返回、后台导出报表、配置文件和日志,很多都以 JSON 形式存在。JSON 适合传输和存储,但字段一多、层级一深,逐条核对、筛选、汇总就很费劲。转成 Excel 后每条记录一行、每个字段一列,可以直接用筛选、排序、求和和数据透视表处理。本文围绕 Python 标准库 json 加 Spire.XLS for Python 的组合,把「读取 JSON → 写入工作簿 → 保存 xlsx」这条链路拆开讲清楚,覆盖基础转换、列顺序固定、嵌套结构展开、表头格式化、列宽自适应和文件夹批量处理几个场景。

一、为什么要做这层转换

原文给出的理由可以归为三点:

1. 便于查看:JSON 嵌套结构下字段多,肉眼核对效率低;转成表格后记录与列一一对应,直观很多。
2. 便于分析:xlsx 可以直接套用筛选、排序、求和、透视表,不必再写第二套统计脚本。
3. 便于交付:Excel 是常见的数据交换载体,自动生成 .xlsx 能省掉手工复制整理。

本质上是把 JSON 里的数据重排成适合二维表展示的结构,行是记录,列是字段。

二、环境准备

JSON 解析用 Python 标准库,不需要额外装包;工作簿的创建、单元格写入和保存由 Spire.XLS 负责:
  1. pip install Spire.XLS
复制代码

导入模块:
  1. import json
  2. from spire.xls import *
  3. from spire.xls.common import *
复制代码

三、基础流程:字典列表写入工作表

最常见的数据形态是「根节点为一个非空列表,列表元素是字段结构基本一致的字典」。json.load() 读文件后直接得到 Python 的 list,取第一条记录的键作为表头,再从第二行开始逐条写入。
  1. import json
  2. from spire.xls import *
  3. from spire.xls.common import *
  4. inputFile = "data.json"
  5. outputFile = "data.xlsx"
  6. with open(inputFile, "r", encoding="utf-8") as f:
  7.     records = json.load(f)
  8. workbook = Workbook()
  9. sheet = workbook.Worksheets[0]
  10. headers = list(records[0].keys())
  11. for col, key in enumerate(headers, start=1):
  12.     sheet.Range[1, col].Text = key
  13. for row, record in enumerate(records, start=2):
  14.     for col, key in enumerate(headers, start=1):
  15.         sheet.Range[row, col].Text = str(record.get(key, ""))
  16. workbook.SaveToFile(outputFile, ExcelVersion.Version2013)
  17. workbook.Dispose()
复制代码

几个关键点:

- sheet.Range[行, 列] 定位单元格,行列都从 1 开始计数,Range[1, 1] 就是 A1。
- 表头写在第 1 行,数据从第 2 行起,enumerate 的 start 参数直接控制起始编号。
- Text 属性按文本赋值,所以用 str() 把 JSON 中的值先转成字符串。
- 这段代码的前提是根节点为非空列表且元素为字典;结构不同就必须调整读取和写入方式。

四、固定列顺序:不要依赖第一条记录的键

直接用 records[0].keys() 有个隐患:不同文件的字段虽然相同,出现顺序却可能不一致,生成的 Excel 列顺序会跟着数据漂移,不利于多份文件合并对比。更稳的做法是提前声明字段列表。
  1. import json
  2. from spire.xls import *
  3. from spire.xls.common import *
  4. inputFile = "users.json"
  5. outputFile = "users.xlsx"
  6. with open(inputFile, "r", encoding="utf-8") as f:
  7.     records = json.load(f)
  8. workbook = Workbook()
  9. sheet = workbook.Worksheets[0]
  10. columns = ["id", "name", "email", "department"]
  11. for col, name in enumerate(columns, start=1):
  12.     sheet.Range[1, col].Text = name
  13. for row, record in enumerate(records, start=2):
  14.     for col, name in enumerate(columns, start=1):
  15.         value = record.get(name, "")
  16.         sheet.Range[row, col].Text = "" if value is None else str(value)
  17. workbook.SaveToFile(outputFile, ExcelVersion.Version2013)
  18. workbook.Dispose()
复制代码

record.get(name, "") 保证字段缺失时以空串填充而不是抛异常;额外的 None 判断则避免把 None 写成字符串 "None"。这种写法适合批量转换结构相似的文件,能让输出的 Excel 保持一致的列布局。

五、嵌套 JSON 展开:递归 + 点号路径

真实数据往往带嵌套:用户记录里有 address 对象,订单里有商品列表。如果直接 str() 整个对象塞进单元格,会得到带括号和引号的字符串,既不能单独筛选也不好分析。

处理思路是先展平:字典递归下钻,用点号把父键和子键连起来(address 中的 city 变成 address.city);列表则用逗号拼成一个字符串,去掉方括号和引号。
  1. import json
  2. from spire.xls import *
  3. from spire.xls.common import *
  4. inputFile = "/sample.json"
  5. outputFile = "/orders.xlsx"
  6. with open(inputFile, "r", encoding="utf-8") as f:
  7.     records = json.load(f)
  8. def flatten(record, prefix=""):
  9.     items = {}
  10.     for key, value in record.items():
  11.         composed = f"{prefix}{key}"
  12.         if isinstance(value, dict):
  13.             items.update(flatten(value, composed + "."))
  14.         elif isinstance(value, list):
  15.             items[composed] = ", ".join(map(str, value))
  16.         else:
  17.             items[composed] = value
  18.     return items
  19. flat_records = [flatten(record) for record in records]
  20. all_keys = []
  21. for record in flat_records:
  22.     for key in record.keys():
  23.         if key not in all_keys:
  24.             all_keys.append(key)
  25. workbook = Workbook()
  26. sheet = workbook.Worksheets[0]
  27. for col, name in enumerate(all_keys, start=1):
  28.     sheet.Range[1, col].Text = name
  29.     sheet.Range[1, col].Style.Font.IsBold = True
  30. for row, record in enumerate(flat_records, start=2):
  31.     for col, name in enumerate(all_keys, start=1):
  32.         value = record.get(name, "")
  33.         sheet.Range[row, col].Text = "" if value is None else str(value)
  34. sheet.AllocatedRange.AutoFitColumns()
  35. workbook.SaveToFile(outputFile, ExcelVersion.Version2013)
  36. workbook.Dispose()
复制代码

这段代码的三个设计选择值得留意:

1. 表头取所有记录字段的并集。只读第一条记录的键,会漏掉后续记录才出现的嵌套字段;遍历展平后的全部记录并去重,能保证出现过的字段都进表头。
2. 可以手动限定字段。如果不需要全量导出,直接把目标列写成列表即可,例如 ["id", "address.city", "address.zip"],只写指定列。
3. 列表值合并为字符串。flatten() 遇到 list 时用 ", ".join(map(str, value)) 拼接,像 ["Python", "SQL", "Excel"] 会变成 Python, SQL, Excel 落在同一个单元格里。

六、表头样式与列宽自适应

数据落盘后,通常要让表头和数据区区分开。Spire.XLS 通过 Style.Font 控制字体样式,IsBold 控制加粗,Color 指定字体颜色:
  1. columns = ["id", "name", "email", "department"]
  2. for col, name in enumerate(columns, start=1):
  3.     sheet.Range[1, col].Text = name
  4.     sheet.Range[1, col].Style.Font.IsBold = True
  5.     sheet.Range[1, col].Style.Font.Color = Color.get_CadetBlue()
  6. for row, record in enumerate(records, start=2):
  7.     for col, name in enumerate(columns, start=1):
  8.         sheet.Range[row, col].Text = str(record.get(name, ""))
复制代码

除字体外,字号、背景填充、对齐方式等属性同样可以按需设置,这里不再展开。

列宽问题单独说一下。JSON 字段值长短不一,默认列宽容易截断内容。写完数据后调用 AutoFitColumns() 按内容自适应:
  1. sheet.AllocatedRange.AutoFitColumns()
  2. workbook.SaveToFile(outputFile, ExcelVersion.Version2013)
  3. workbook.Dispose()
复制代码

AllocatedRange 表示工作表中已分配的单元格区域,用它比手写固定范围更省事,数据量变化时不用改代码。

七、批量转换文件夹中的 JSON

文件多了就交给 os 遍历:读一个 JSON、建一个 Workbook、存一个 xlsx,然后释放。
  1. import json
  2. import os
  3. from spire.xls import *
  4. from spire.xls.common import *
  5. inputFolder = "./json_files"
  6. outputFolder = "./excel_files"
  7. if not os.path.exists(outputFolder):
  8.     os.makedirs(outputFolder)
  9. for name in os.listdir(inputFolder):
  10.     if not name.lower().endswith(".json"):
  11.         continue
  12.     inputPath = os.path.join(inputFolder, name)
  13.     outputPath = os.path.join(
  14.         outputFolder,
  15.         os.path.splitext(name)[0] + ".xlsx"
  16.     )
  17.     with open(inputPath, "r", encoding="utf-8") as f:
  18.         records = json.load(f)
  19.     workbook = Workbook()
  20.     sheet = workbook.Worksheets[0]
  21.     columns = list(records[0].keys())
  22.     for col, key in enumerate(columns, start=1):
  23.         sheet.Range[1, col].Text = key
  24.         sheet.Range[1, col].Style.Font.IsBold = True
  25.     for row, record in enumerate(records, start=2):
  26.         for col, key in enumerate(columns, start=1):
  27.             value = record.get(key, "")
  28.             sheet.Range[row, col].Text = "" if value is None else str(value)
  29.     sheet.AllocatedRange.AutoFitColumns()
  30.     workbook.SaveToFile(outputPath, ExcelVersion.Version2013)
  31.     workbook.Dispose()
复制代码

要点:os.listdir() 拿到目录条目后用 lower().endswith(".json") 过滤,文件名大小写混用也不会漏;os.path.splitext(name)[0] 去掉原扩展名再拼 .xlsx,同名文件不会互相覆盖。这个示例仍是简单字典列表结构,如果目录里的 JSON 结构不一致或含嵌套对象,要按前面介绍的展平逻辑改造字段提取部分。

八、容易踩坑的几处细节

- 数据结构先确认。基础转换和批量处理都基于「非空字典列表」这一前提。根节点是单个字典,或者存在多层嵌套时,读取和写入逻辑都得改。
- 列顺序按需固定。只需部分字段时,提前定义列名列表,别直接摊开 JSON 的全部键,多份文件转换后的列序才稳定。
- 单元格类型要清楚。示例统一用 Text 写入,数字和日期也会变成文本;如果后续要在 Excel 里做数值计算,得换成对应的单元格赋值方式。
- 读文件指定编码。open() 加 encoding="utf-8" 才能正确读入含中文等非 ASCII 字符的 JSON,减少乱码。
- 批量时及时释放。每个工作簿保存完调用 Dispose(),文件数量大时才不会持续占用内存等资源。

整体流程可以概括为:json.load() 读数据 → 确定列(取自键、固定列表或展平后的并集)→ 用 sheet.Range[行, 列].Text 逐格写入 → 按需设置表头样式和列宽 → SaveToFile() 落盘 → Dispose() 收尾。遇到嵌套结构,在读取和写入之间插入一层递归展平即可,其余代码几乎不用动。
回复

使用道具 举报

发表于 18 分钟前 | 显示全部楼层

Re: Python读取JSON转Excel:嵌套展开与批量转换脚本

楼主这条链路拆得挺清楚的,从基础写入到固定列顺序再到嵌套展开,层层递进,照着改就能用。固定列顺序那段提醒很实在,依赖 records[0].keys() 在多文件批量场景下确实容易让列顺序漂移,提前声明 columns 省心不少;record.get 加 None 判断这个细节也考虑到了。 有两个小地方想提醒一下:一是统一用 Text 属性写单元格,数字和日期都会被当作文本存进去,后面在 Excel 里求和、排序或者做透视表时可能不按数值处理,如果原始数据里有数值字段,可能得按类型分别赋值或者写入后转一下格式。二是嵌套里的列表用逗号拼成字符串,如果列表元素本身带逗号,或者列表里还是对象,展开后信息容易混在一起,最好约定个分隔符或者给元素加序号,方便回溯。 另外批量处理那块,建议把 workbook.Dispose() 放进 finally 里,避免某个文件解析出错时工作簿没释放、后面文件被占用。根节点不是列表或者遇到空列表时也可以先做个类型判断再走流程,不然 records[0] 会直接报错。 帖子里列宽自适应和文件夹批量那部分好像没展开完,期待后续补上,这块在实际批量转报表时最常用。
回复 支持 反对

使用道具 举报

发表于 18 分钟前 | 显示全部楼层

Re: Python读取JSON转Excel:嵌套展开与批量转换脚本

感谢分享,这条链路拆得挺清楚的,尤其几个容易踩的坑都点到了。 固定列顺序那一段我觉得最有价值。用第一批数据的 keys 当表头,看着省事,但多份文件一起处理时列顺序一旦漂移,后面合并或者做透视表就得反复对列,提前声明字段列表确实稳得多。另外 record.get 之后对 None 单独判断这点也很细,不然 Excel 里会冒出一堆字符串 "None",筛选和统计都容易出错。 不过嵌套展开那一节的代码好像贴到一半断了,只到读取文件的 open 就没了,后面的展平函数和写入逻辑没看到。光看前面文字描述,递归下钻、点号连路径、列表拼字符串这个思路是合理的,但如果列表里的元素本身还是字典,简单用逗号拼接会把子字段的键丢掉,展开后就只剩一串看不出结构的值,这种数据可能还得再定义一下拼接规则或者干脆拆成多行。方便的话把递归展平和写入的完整代码补上,我也想照着跑一遍验证一下。
回复 支持 反对

使用道具 举报

发表于 18 分钟前 | 显示全部楼层

Re: Python读取JSON转Excel:嵌套展开与批量转换脚本

整体思路很完整,把“读 JSON、写工作簿、保存 xlsx”这条链路拆成基础转换、固定列顺序、嵌套展开、表头格式化、列宽自适应和批量处理几个场景,读起来有章法。特别是固定列顺序那节,不依赖 records[0].keys(),而是显式声明 columns,再用 get 加 None 判断兜底,这个细节对批量转换很关键,避免多份文件列布局漂移后没法横向对比。 有两个地方想接着聊聊。一是所有值都用 Text 属性按文本写入,数字和日期也会变成文本格式,到了 Excel 里求和、排序、日期筛选可能都不太灵。可以在写单元格前判断一下类型,整数、浮点用数值属性写,日期单独处理,只有真正需要保留原样的字段才按文本写。二是嵌套展开时把列表用逗号拼成字符串,如果元素本身带逗号,回读或拆分就会有歧义,遇到这种字段可以先统一序列化成 JSON 字符串,或者换个不常见分隔符,后面要还原也方便些。 另外基础流程里假设根节点是非空列表且元素为字典,实际接口返回经常是包了一层对象的,比如外层有 code、data、total 这种结构。可以在读取后加一段判断,如果根是字典就先定位到真正的记录列表,或者直接走递归展平逻辑,脚本通用性会更好。批量处理那部分后续如果能补上,建议加个遍历文件夹时跳过临时文件、对单个文件失败不中断整批的处理,不然一个坏文件就把整批卡住了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-11-13 02:23 , Processed in 0.027119 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部