分享页面
技术分享 / Python将JSON转换为CSV:数据格式转换脚本

Python将JSON转换为CSV:数据格式转换脚本

技术分享

为什么一个”简单的格式转换”会写成工程问题?

绝大多数技术文档把JSON转CSV讲成三步:json.loads()解析、提取字段、csv.writer()写出。逻辑没错,但只对结构干净、字段固定、编码统一的JSON成立。

实际业务中的JSON来自批量数据采集,每一条记录的嵌套深度、字段完整度、编码方式都可能不同。一个商品列表接口返回的JSON,第一页是{"price": 29.9},第二页变成{"price": {"current": 29.9, "original": 39.9}},第三页直接把price字段丢了。脚本要兜住这三种情况,复杂度不是线性增长,是指数级的。

技术团队的第一反应通常是”换更高级的库”或”写更精巧的解析逻辑”。但真正的工程卡点不在Python写法,而在上游数据结构本身。下面逐个展开三个卡点。

JSON结构不可控的三个卡点,根源在哪?

卡点 表现 根源
嵌套深度不定 同一字段在不同记录中嵌套层级不同,flatten逻辑被迫递归 数据源返回的JSON格式在分页或不同时段有差异
字段随机缺失 部分记录缺少某些字段,CSV列对齐失败 数据源在部分条件下省略可选字段,采集层未做默认值填充
编码混杂 UTF-8中混入GBK或Latin-1字符,解析报错或乱码 数据源返回的Content-Type与实际编码不一致,采集层未做编码归一化

三个卡点的共性:问题发生在转换阶段,但根源在采集阶段。采集层拿到的JSON越干净、结构越一致,转换脚本就越简单。

这不是”Python技巧不够”,是上游数据质量不可控的代价在下游集中爆发。

Python脚本怎么写,才能兜住结构不可控的JSON?

即使明确了根源在采集层,转换脚本仍然需要兜底。以下是一个可复用的框架,覆盖嵌套flatten、字段对齐、编码预处理三个环节。

环节一:递归flatten

嵌套JSON需要展平为一维键值对,键名用下划线拼接父子层级。

def flatten_json(obj, parent_key='', sep='_'):
    items = {}
    if isinstance(obj, dict):
        for k, v in obj.items():
            new_key = f"{parent_key}{sep}{k}" if parent_key else k
            items.update(flatten_json(v, new_key, sep))
    elif isinstance(obj, list):
        for i, v in enumerate(obj):
            items.update(flatten_json(v, f"{parent_key}{sep}{i}", sep))
    else:
        items[parent_key] = obj
    return items

关键判断:list类型的展平策略要按业务决定。商品标签["电子", "手机"]展平为tags_0、tags_1是可以的;但如果list里嵌套了完整对象,展平后CSV列数会失控,这时应该把该字段序列化为JSON字符串存入单列。

环节二:字段对齐

不同记录的字段集合不一致时,需要先扫一遍所有记录收集全量字段,再用全量字段做CSV表头,缺失字段填默认值。

import csv
import json

def json_to_csv(json_list, output_path):
    flat_rows = [flatten_json(record) for record in json_list]
    all_keys = set()
    for row in flat_rows:
        all_keys.update(row.keys())
    all_keys = sorted(all_keys)

    with open(output_path, 'w', newline='', encoding='utf-8-sig') as f:
        writer = csv.DictWriter(f, fieldnames=all_keys, extrasaction='ignore')
        writer.writeheader()
        for row in flat_rows:
            writer.writerow({k: row.get(k, '') for k in all_keys})

注意encoding='utf-8-sig':加BOM头是为了让Excel正确识别UTF-8编码。这个细节在数据分析团队拿CSV做下游处理时经常踩坑。

环节三:编码预处理

采集返回的原始字节流,在json.loads()之前做一次编码归一化:

import chardet

def safe_decode(raw_bytes):
    detected = chardet.detect(raw_bytes)
    encoding = detected.get('encoding', 'utf-8')
    return raw_bytes.decode(encoding, errors='replace')

errors='replace'保证即使编码检测不准,也不会因为单个非法字符阻断整个流程。但replace会丢失信息,如果业务对字段精确度要求高(比如药品数据采集、法律大数据),应该记录替换日志,人工复核。

三个环节串起来就是一个可复用的转换框架。但框架能解决的是”兜底”,不能解决”根因”。

大文件和流式处理怎么做?

上面的框架把所有记录加载到内存再写CSV,对万条级别的数据够用。但企业级数据采集的JSON文件动辄几百MB甚至GB级别,全量加载会直接打爆内存。pandas加载JSON时内存占用约为原始文件大小的3-5倍,一个500MB的JSON文件需要1.5-2.5GB内存。

两条路:

方案 适用场景 Python库 内存占用 处理速度
流式解析 JSON文件是一个大数组,需要逐条处理 ijson 低(MB级) 中等
逐行读取 JSON已是JSONL格式(每行一条独立JSON) 内置readline 低(MB级) 快

流式解析示例(ijson):

import ijson

def stream_json_to_csv(json_path, output_path):
    all_keys = set()
    buffer = []

    with open(json_path, 'rb') as f:
        for record in ijson.items(f, 'item'):
            flat = flatten_json(record)
            all_keys.update(flat.keys())
            buffer.append(flat)

            if len(buffer) >= 5000:
                flush_to_csv(buffer, all_keys, output_path)
                buffer.clear()

    if buffer:
        flush_to_csv(buffer, all_keys, output_path)

这里有一个工程取舍:流式解析时,第一批5000条记录里收集到的字段集合可能不完整,后续批次出现新字段时CSV表头需要补列。实际操作中,建议先做一次轻量级的字段扫描(只提取key不存值),拿到全量字段后再做第二遍流式写入。两遍扫描的时间成本大约增加30%-50%,但能避免CSV列错位。这个成本在生产环境里是值得的。

如果数据采集阶段可以控制输出格式,建议直接输出JSONL(每行一条独立JSON),转CSV时只需逐行json.loads() + 写入,不依赖ijson等第三方库。

采集层的结构可控性,能省多少转换成本?

回到本篇的判断轴:转换脚本的复杂度,本质上是采集层返回数据结构不可控的代价。

把上面四个环节的工程量拆开看:

环节 结构可控时的工作量 结构不可控时的工作量 差异来源
递归flatten 固定一层,不需要递归 递归+list展平策略判断 嵌套深度是否稳定
字段对齐 字段固定,直接写表头 全量扫描+缺失填充 字段集合是否一致
编码预处理 统一UTF-8,不需要chardet 逐条检测+替换+日志 编码是否归一
大文件处理 按固定schema分块写入 两遍扫描+动态补列 schema是否可预测

我们青果网络在服务网站采集器、APP大数据分析这类场景时(来源:青果实践观测,2024-2025,样本=数百家),观察到一个反复出现的规律:当采集层的IP调度稳定(可用率99.9%、平均延迟<100ms,来源:青果网络官网)、请求节奏与目标站点的访问规则匹配时,返回的JSON结构一致性明显更高。

原因不复杂:IP频繁异常切换导致的请求上下文丢失、带宽波动引起的响应截断、混池导致的出口环境差异,这些都会让同一接口在不同请求中返回不同结构的JSON。采集层的稳定性问题,最终以”JSON结构漂移”的形式传导到格式转换阶段。

当然,采集层再稳定,也不能保证数据源本身不做格式变更。转换脚本的防御性逻辑仍然必要,但防御的范围和频率会大幅缩减。

回到采集层,可以使用青果哪款代理IP?

JSON转CSV的工程成本,核心不在Python怎么写,而在上游采集返回的JSON结构是否可控。

基于这条判断,选型落到我们青果网络的短效代理:做网站采集器、APP大数据分析这类高频公开数据采集,国内短效代理按量提取,1万IP档27元起、单价低至0.00216元/IP(50万IP档),存活1分钟,单次提取上限200(来源:青果网络官网)。高频采集对IP轮换速度和纯净度要求高,短效代理的快速轮换节奏与这类场景天然匹配;配合业务分池技术,不同采集任务走不同IP子池,避免跨任务的出口环境差异导致JSON结构漂移。

回到数据工程的完整链路里看,真正在做的判断不是”用什么Python库”,是”数据管道的第一公里要不要花精力管住结构一致性”——前者是脚本技巧,后者是工程决策。

常见问题

Q1:json模块和pandas读JSON,哪个更适合做CSV转换?

取决于数据规模和结构复杂度。10万条以内、嵌套不超过两层的JSON,标准库json + csv就够,依赖少、启动快。超过10万条或嵌套超过三层,pandas.json_normalize()能自动展平嵌套结构并对齐字段,代码量减少约60%。但pandas的内存占用约为原始文件大小的3-5倍,GB级文件需要搭配chunksize参数分块处理。

Q2:嵌套JSON展平后列名太长怎么办?

列名由父子键用分隔符拼接,嵌套四五层后列名可能变成data_items_0_attributes_price_current这种长度。两种处理思路:一是在flatten函数里设置最大递归深度,超过深度的子结构直接序列化为JSON字符串;二是在输出前做一次列名映射,把长列名替换为业务简称。前者保证数据不丢失,后者保证可读性,业务上通常先用前者跑通,再按分析需求做后者。

Q3:CSV用Excel打开出现乱码,怎么解决?

最常见的原因是CSV文件用UTF-8编码但没加BOM头。Excel默认用系统编码(中文Windows是GBK)打开CSV,UTF-8无BOM的文件会乱码。解决方法是写入时指定encoding='utf-8-sig',sig就是BOM签名。如果CSV已经生成了,可以用文本编辑器重新保存为UTF-8 with BOM格式。

Q4:JSONL格式和标准JSON数组,转CSV时有什么区别?

标准JSON数组(整个文件是一个[{...}, {...}])需要解析器把整个文件读进内存才能遍历。JSONL(每行一条独立JSON)可以逐行读取,内存占用恒定。如果数据采集阶段可以控制输出格式,建议直接输出JSONL,转CSV时只需逐行json.loads() + 写入,不依赖第三方库,对GB级文件也能平稳处理。

Q5:采集回来的JSON有些记录是HTML错误页而不是数据,怎么过滤?

这是采集层请求失败但HTTP状态码仍返回200的情况。在转换前加一步校验:检查每条记录是否包含业务必需字段(比如商品数据必须有id和name),不包含的记录写入错误日志而不是CSV。我们青果网络在服务网站采集器场景时,建议客户在采集脚本里先做响应格式校验,把非JSON响应过滤在采集阶段而不是转换阶段,减少下游数据清洗的工作量。

Q6:转换后的CSV文件太大,下游处理不动怎么办?

按行数拆分是最简单的方式,每50万行或100万行切割一个文件。如果下游分析有分组需求(按日期、按类别),在转换时就按分组字段分流到不同CSV文件,比事后拆分效率更高。Python标准库的csv.writer配合字典做文件句柄池即可实现,不需要额外依赖。

推荐阅读
手把手教你配置 SOCKS5 代理,新手小白也能上手!

当谈到代理技术时,SOCKS5代理凭借其高兼容性、快速的传输性能和广泛的适用场景,成为了许多用户与开发者的首选。不管你是想提升爬虫数据采集的成功率,还是优化跨区域网络连接,都需要配置SOCKS5代理。今天,我们将带你从零开始学习SOCKS5代理的配置方法。不论你是小白用户第一次接触代理,还是需要优化...

2025-01-23
SOCKS5代理IP
代理http的是什么,有什么用

在数据驱动的世界里,无论是企业还是个人开发者,都离不开数据采集。采集数据信息不仅帮助我们了解市场动态,更支撑了大数据分析、机器学习等前沿技术的发展。然而,随着数据量的不断增大以及网络环境的复杂性,如何保证数据采集的效率和稳定性已经成为了一项不可忽视的挑战。而代理HTTP在当中扮演了重要的角色。 ...

2024-08-02
HTTP代理
国外代理ip节点哪里购买?高质量海外代理购买方法

您是否需要一篇深入且实用的指南来解答“国外代理IP节点哪里购买”的问题?网络时代,各类用户对国外代理IP的需求逐渐增加,无论是开发者、数据职业者,还是企业级用户,选择高质量的海外代理IP至关重要。那么,如何才能购买到适合自身需求的高质量代理IP呢?这篇文章将从购买渠道、选择要点和使用方法等方面为您详...

2025-07-24
国外代理IP
3分钟了解:静态IP和动态IP的区别具体在哪里?

如果你是一名开发者、网络管理员,或者是对网络技术感兴趣的小伙伴,想必你时常会听到“静态IP”和“动态IP”这两个名词。它们在互联网世界里扮演着重要角色,却存在显著差异。在日常工作的不同场景中,如何正确选择两者,才能更高效地解决实际问题? 本文将带你在3分钟内轻松搞懂静态IP和动态IP的主要区别...

2025-01-16
动态代理IP 静态代理IP
IP 隧道深度解析:原理、优势与实战选型

很多团队第一次接触“IP 隧道”(也常被称作“隧道代理”)时,会把它与传统网络中的 GRE、IPsec 等隧道概念混为一谈。二者确有渊源,却服务于不同目标:网络隧道更偏向专网互联与封装转发;而代理领域的 IP 隧道,则是通过一个稳定的入口节点,把后端海量出口 IP 的调度、轮换与质量控制“藏在门后”...

2025-09-30
http隧道代理 隧道代理IP
深入解析动态ip:什么是动态IP?动态IP与静态IP对比

在日常生活和企业应用中,IP地址是每台设备接入互联网的“身份标识”。大多数人每天使用的其实并不是固定不变的地址,而是运营商临时分配的 动态IP。与静态IP相比,动态IP更普遍、更经济,也更适合普通用户和一些大规模任务。本文将深入解析动态IP的特征及应用场景。 1 什么是动态IP 动态I...

2025-09-23
静态代理IP 动态代理IP
扫码添加专属客服
扫码关注公众号