第37课:Python|JSON/CSV/XML数据解析与序列化全套实战教程

在这里插入图片描述


📖 开篇导读

在之前的课程中,我们学习了字符串、文件操作、正则表达式等,能够处理基本的文本数据。但在现实开发中,数据往往以结构化格式进行交换和存储,最常见的三种格式是:JSONCSVXML

  • JSON(JavaScript Object Notation):轻量级数据交换格式,广泛用于 Web API、配置文件、NoSQL 数据库。易读、易解析,是前后端通信的事实标准。
  • CSV(Comma-Separated Values):表格数据的纯文本表示,Excel、数据库导入导出、数据分析中无处不在。
  • XML(eXtensible Markup Language):标记语言,常用于配置文件(如 Java 项目)、SOAP 协议、Office 文档等。

Python 内置了强大的模块来处理这三种格式:jsoncsvxml.etree.ElementTree。掌握它们,你就可以轻松地从 API 获取数据、导出报表、解析复杂文档。

💡 工作场景

  • 后端开发:接收前端 JSON 请求,返回 JSON 响应;读取配置文件(JSON/YAML);导出 CSV 报表。
  • 数据分析:读取 CSV 数据集,处理后保存为 JSON。
  • 爬虫:解析 XML 格式的网站地图、RSS 订阅。
  • 自动化:将 Excel 转换为 CSV,批量处理配置文件。

本课将系统学习:

  • JSON 的序列化(dump/dumps)与反序列化(load/loads),处理自定义对象。
  • CSV 的读取(reader/DictReader)与写入(writer/DictWriter),处理不同方言。
  • XML 的解析与生成(ElementTree),XPath 基本使用。
  • 实战:API 数据抓取、CSV 报表生成、XML 配置文件读写。

学完本课,你将能够自如地在这些数据格式之间转换,应对工作中的各种数据需求。


🎯 学习目标

目标编号具体掌握内容对应面试/工作价值
1️⃣掌握 JSON 的编码dumps)和解码loads),以及文件读写Web API 交互基础
2️⃣处理 JSON 中的自定义对象defaultobject_hook序列化复杂数据
3️⃣读写 CSV 文件,使用 csv.reader/writer 以及字典形式数据导出导入
4️⃣处理 CSV 方言(delimiter、quotechar)和特殊字符兼容各种格式
5️⃣解析和生成 XML,使用 ElementTree 遍历/查找/修改元素配置文件、旧系统集成
6️⃣掌握 XPath 基本表达式find/findall)定位 XML 元素高效提取数据

🔥 面试考点:“json.loadsjson.dumps 的区别?”“如何将自定义对象序列化为 JSON?”“CSV 读写时如何处理引号和逗号?”“XML 解析的几种方式?”


📚 知识点理论精讲

一、JSON 数据格式

JSON 是 JavaScript 对象表示法的子集,但已被几乎所有语言支持。它基于键值对和有序列表。

1.1 JSON 支持的数据类型

JSON 类型Python 类型
对象 {}字典 dict
数组 []列表 list
字符串 "..."字符串 str
数字 123 / 12.3整数 int / 浮点 float
布尔值 true/falseTrue/False
nullNone

1.2 json 模块核心函数

  • json.dumps(obj, indent=None, ensure_ascii=True, ...):将 Python 对象序列化为 JSON 字符串。
  • json.loads(json_str):将 JSON 字符串反序列化为 Python 对象。
  • json.dump(obj, file, ...):将 Python 对象序列化并写入文件。
  • json.load(file):从文件中读取 JSON 并反序列化。

1.3 常用参数

  • indent:缩进空格数,美化输出。
  • ensure_ascii:默认为 True,将非 ASCII 字符转义为 \uXXXX;设为 False 可输出中文。
  • sort_keys:按键排序输出。

1.4 自定义对象序列化

通过 default 参数指定一个函数,用于将自定义类型转换为可序列化的字典。

def custom_encoder(obj):
    if isinstance(obj, Person):
        return {"name": obj.name, "age": obj.age}
    raise TypeError

json_str = json.dumps(person, default=custom_encoder)

反序列化时使用 object_hook 将字典转换为对象。

二、CSV 数据格式

CSV 是逗号分隔值,每行代表一条记录,字段间用分隔符(通常是逗号)隔开。也可以使用其他分隔符(如制表符、分号)。

2.1 csv 模块核心读写

  • csv.reader(file, dialect='excel', **fmtparams):返回一个读取器,每行返回列表。
  • csv.writer(file, dialect='excel', **fmtparams):返回写入器,支持 writerowwriterows
  • csv.DictReader(file):将第一行作为列名,每行返回字典。
  • csv.DictWriter(file, fieldnames):写入字典数据。

2.2 常用参数

  • delimiter:分隔符,默认为 ,
  • quotechar:引用字符,默认为 "
  • quoting:引用方式(csv.QUOTE_MINIMALcsv.QUOTE_ALL 等)。
  • lineterminator:行终止符,默认为 \r\n

2.3 处理含逗号字段

自动用双引号包裹包含分隔符或换行符的字段。

三、XML 数据格式

XML 是可扩展标记语言,使用标签定义元素,可嵌套,自描述。

3.1 XML 结构示例

<people>
    <person id="1">
        <name>张三</name>
        <age>25</age>
    </person>
</people>

3.2 xml.etree.ElementTree 模块

  • ElementTree.parse(file):从文件解析 XML 得到树对象。
  • ElementTree.fromstring(xml_str):从字符串解析。
  • getroot():获取根元素。
  • 元素方法:tagattribtextfind()findall()iter()

3.3 查找元素

  • find(tag):返回第一个匹配的子元素。
  • findall(tag):返回所有匹配的子元素列表。
  • iter(tag):递归遍历所有匹配的元素(可省略 tag 遍历所有)。

支持 XPath 简单表达式,如 ".//name" 表示所有后代中的 name 元素。

3.4 创建与修改 XML

root = ET.Element("root")
child = ET.SubElement(root, "child")
child.text = "value"
tree = ET.ElementTree(root)
tree.write("output.xml", encoding="utf-8", xml_declaration=True)

💻 代码案例实操

案例1:JSON 基础——字典与文件互转

"""
json_basic.py
演示 JSON 的基础序列化与反序列化
"""

import json

# Python 对象
data = {
    "name": "张三",
    "age": 25,
    "hobbies": ["reading", "coding"],
    "is_student": False,
    "address": None
}

# 序列化为 JSON 字符串
json_str = json.dumps(data, indent=4, ensure_ascii=False)
print("JSON 字符串:")
print(json_str)

# 反序列化为 Python 对象
obj = json.loads(json_str)
print("反序列化后:", obj)
print(type(obj))  # dict

# 写入文件
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, indent=4, ensure_ascii=False)

# 从文件读取
with open("data.json", "r", encoding="utf-8") as f:
    loaded = json.load(f)
print("从文件加载:", loaded)

案例2:自定义对象 JSON 序列化

"""
custom_json_encoder.py
使用 default 和 object_hook 处理自定义对象
"""

import json

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

def person_encoder(obj):
    """自定义编码器:将 Person 转为字典"""
    if isinstance(obj, Person):
        return {"__type__": "Person", "name": obj.name, "age": obj.age}
    raise TypeError

def person_decoder(dct):
    """自定义解码器:将字典转为 Person"""
    if dct.get("__type__") == "Person":
        return Person(dct["name"], dct["age"])
    return dct

# 序列化
p = Person("李四", 30)
json_str = json.dumps(p, default=person_encoder, indent=2)
print("序列化结果:", json_str)

# 反序列化
p2 = json.loads(json_str, object_hook=person_decoder)
print("反序列化后:", p2.name, p2.age)

案例3:JSON 与 API 交互——获取天气信息

"""
api_json.py
模拟从 API 获取 JSON 数据并解析(使用 requests 库)
"""

import json
import requests

# 示例:调用免费天气 API(实际需替换真实 URL)
def fetch_weather(city):
    # 这里使用一个模拟的 JSON 响应代替真实请求
    mock_response = {
        "city": city,
        "temperature": 22.5,
        "unit": "C",
        "condition": "Sunny",
        "forecast": [
            {"day": "Monday", "temp": 23},
            {"day": "Tuesday", "temp": 21}
        ]
    }
    # 实际开发中: response = requests.get(f"https://api.weather.com?city={city}")
    # return response.json()
    return mock_response

# 获取并解析
weather = fetch_weather("Beijing")
print(f"城市: {weather['city']}, 温度: {weather['temperature']}{weather['unit']}")

# 保存到文件
with open("weather.json", "w") as f:
    json.dump(weather, f, indent=2)

案例4:CSV 读写基础

"""
csv_basic.py
读写 CSV 文件,包括列表和字典形式
"""

import csv

# 写入 CSV (列表形式)
data = [
    ["姓名", "年龄", "城市"],
    ["张三", 25, "北京"],
    ["李四", 30, "上海"],
    ["王五", 28, "广州"]
]

with open("people.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerows(data)

# 读取 CSV (列表形式)
with open("people.csv", "r", encoding="utf-8") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

# 写入 CSV (字典形式,带表头)
people = [
    {"name": "赵六", "age": 35, "city": "深圳"},
    {"name": "周七", "age": 32, "city": "成都"}
]

with open("people_dict.csv", "w", newline="", encoding="utf-8") as f:
    fieldnames = ["name", "age", "city"]
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(people)

# 读取字典形式 CSV
with open("people_dict.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(f"{row['name']} - {row['age']}岁 - {row['city']}")

案例5:处理 CSV 特殊字符和自定义分隔符

"""
csv_special.py
处理含逗号、换行符的字段,以及使用分号分隔符
"""

import csv

# 包含逗号的数据
data = [
    ["姓名", "描述"],
    ["张三", "爱好:阅读,编程"],   # 描述中包含逗号
    ["李四", "多行\n描述"]         # 包含换行符
]

# 写入时自动引用特殊字符
with open("special.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f, quoting=csv.QUOTE_MINIMAL)  # 默认行为
    writer.writerows(data)

# 用分号作为分隔符写入
semicolon_data = [["a", "b"], ["c", "d"]]
with open("semicolon.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f, delimiter=';')
    writer.writerows(semicolon_data)

# 读取分号分隔的文件
with open("semicolon.csv", "r", encoding="utf-8") as f:
    reader = csv.reader(f, delimiter=';')
    for row in reader:
        print(row)

案例6:XML 解析与遍历

"""
xml_parse.py
使用 ElementTree 解析 XML 文件
"""

import xml.etree.ElementTree as ET

# 示例 XML 字符串
xml_str = """
<bookstore>
    <book category="fiction">
        <title lang="en">Python编程</title>
        <author>张三</author>
        <price>59.90</price>
    </book>
    <book category="tech">
        <title lang="zh">深度学习入门</title>
        <author>李四</author>
        <price>89.00</price>
    </book>
</bookstore>
"""

# 从字符串解析
root = ET.fromstring(xml_str)

# 遍历所有 book 元素
for book in root.findall("book"):
    title = book.find("title").text
    author = book.find("author").text
    price = book.find("price").text
    category = book.get("category")
    print(f"《{title}》作者:{author}, 价格:{price}, 分类:{category}")

# 使用 XPath 查找所有标题(任意层级)
titles = root.findall(".//title")
for title in titles:
    print("标题:", title.text, "语言:", title.get("lang"))

案例7:修改和创建 XML 文件

"""
xml_create_modify.py
生成 XML 并写入文件,修改已有 XML
"""

import xml.etree.ElementTree as ET

# 创建 XML 树
root = ET.Element("students")
for i, name in enumerate(["张三", "李四", "王五"], 1):
    student = ET.SubElement(root, "student", id=str(i))
    name_elem = ET.SubElement(student, "name")
    name_elem.text = name
    score = ET.SubElement(student, "score")
    score.text = str(85 + i * 2)

tree = ET.ElementTree(root)
tree.write("students.xml", encoding="utf-8", xml_declaration=True)
print("已生成 students.xml")

# 修改已有的 XML
tree = ET.parse("students.xml")
root = tree.getroot()
# 将所有人的分数加5分
for score_elem in root.findall(".//score"):
    new_score = int(score_elem.text) + 5
    score_elem.text = str(new_score)
# 添加新学生
new_student = ET.SubElement(root, "student", id="4")
name_elem = ET.SubElement(new_student, "name")
name_elem.text = "赵六"
score_elem = ET.SubElement(new_student, "score")
score_elem.text = "95"

tree.write("students_modified.xml", encoding="utf-8", xml_declaration=True)
print("已保存修改后的文件")

案例8:XML 与 JSON 互转

"""
xml_json_convert.py
将 XML 转换为 JSON 格式(简单映射)
"""

import json
import xml.etree.ElementTree as ET

def xml_to_dict(element):
    """递归将 XML 元素转为字典/列表结构"""
    result = {}
    # 处理属性
    if element.attrib:
        result["@attributes"] = element.attrib
    # 处理子元素
    children = list(element)
    if children:
        child_dict = {}
        for child in children:
            child_data = xml_to_dict(child)
            if child.tag in child_dict:
                # 多个同名子元素合并为列表
                if not isinstance(child_dict[child.tag], list):
                    child_dict[child.tag] = [child_dict[child.tag]]
                child_dict[child.tag].append(child_data)
            else:
                child_dict[child.tag] = child_data
        result.update(child_dict)
    else:
        # 无子元素,存放文本内容
        result["#text"] = element.text.strip() if element.text else ""
    return result if result else element.text

# 示例
xml_str = "<person id='1'><name>张三</name><age>25</age></person>"
root = ET.fromstring(xml_str)
data = xml_to_dict(root)
json_str = json.dumps(data, indent=2, ensure_ascii=False)
print("转换后的 JSON:\n", json_str)

案例9:CSV 转 JSON(数据清洗)

"""
csv_to_json.py
将 CSV 文件转换为 JSON 格式
"""

import csv
import json

def csv_to_json(csv_file, json_file):
    data = []
    with open(csv_file, "r", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            # 可选:转换数据类型
            if "age" in row:
                row["age"] = int(row["age"])
            data.append(row)
    with open(json_file, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)

# 创建测试 CSV
with open("sample.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["name", "age", "city"])
    writer.writerow(["张三", "25", "北京"])
    writer.writerow(["李四", "30", "上海"])

csv_to_json("sample.csv", "output.json")
print("转换完成,查看 output.json")

案例10:处理大型 XML(迭代解析)

"""
xml_iterparse.py
使用 iterparse 逐元素解析大型 XML,避免内存爆炸
"""

import xml.etree.ElementTree as ET

# 模拟生成一个大 XML 文件(实际中可能是大日志)
def create_large_xml():
    root = ET.Element("root")
    for i in range(1000):
        child = ET.SubElement(root, "item", id=str(i))
        child.text = f"内容{i}"
    tree = ET.ElementTree(root)
    tree.write("large.xml", encoding="utf-8")

create_large_xml()

# 使用 iterparse 流式处理
def process_large_xml(file):
    for event, elem in ET.iterparse(file, events=("end",)):
        if elem.tag == "item":
            print(f"处理: {elem.get('id')} - {elem.text}")
            # 处理完后清除元素,释放内存
            elem.clear()
    # 清除根元素
    for event, elem in ET.iterparse(file, events=("start",)):
        if elem.tag == "root":
            elem.clear()

process_large_xml("large.xml")

⚠️ 易错点避坑总结

序号坑点描述后果解决方案
1JSON 中键名必须用双引号单引号 JSON 字符串无法解析使用 json.loads,不要手动拼接
2json.dump 写入文件时忘记指定 encoding='utf-8'中文可能乱码显式指定编码
3CSV 读取时未指定 newline=''可能产生空行open() 中添加 newline=''
4包含逗号字段未使用引用CSV 列错位写入时使用 csv.QUOTE_MINIMAL(默认自动)或 QUOTE_ALL
5csv.DictWriter 未先 writeheader缺少列名行显式调用 writeheader()
6XML 命名空间处理XPath 查找失败使用 {namespace}tag 或注册命名空间
7XML 解析时未处理多级命名空间找不到元素使用 root.findall('.//{http://example.com}tag')
8iterparse 没有适时 clear() 导致内存增长大文件内存耗尽处理完每个元素后调用 elem.clear()
9XML 属性与子元素同名冲突转换字典时覆盖区分存取(如加 @ 前缀)
10CSV 方言不一致(如 Excel 导出使用分号)读取失败使用 delimiter=';' 参数

📝 课后实战练习题

第1题:JSON 配置文件读写

创建一个 config.json,包含 hostportdebug 字段。编写程序读取并修改 port 值,然后保存回文件。

第2题:CSV 成绩统计

有一个 scores.csv,列名为 name, math, english, chinese。读取 CSV,计算每个学生的总分和平均分,将结果输出到 report.csv,增加 totalaverage 列。

第3题:XML 解析 RSS 订阅源

使用 xml.etree.ElementTree 解析一个 RSS 订阅源(如 http://feeds.bbci.co.uk/news/rss.xml),提取每个 <item> 的标题、链接、发布时间,打印出来。

第4题:JSON 转换为 CSV

https://jsonplaceholder.typicode.com/users 获取用户 JSON 数据,将其关键字段(id, name, email, phone)导出到 users.csv

第5题:XML 生成配置文件

根据给定的字典数据,生成 XML 配置文件,如:

config = {
    "server": {"host": "localhost", "port": 8080},
    "logging": {"level": "INFO", "file": "app.log"}
}

生成对应的 XML 表示。

第6题:CSV 处理大文件分块

编写程序,使用 csv.reader 分块读取一个大 CSV 文件(每块1000行),对每块进行统计(如某列总和),最后输出总量。展示内存高效处理。

第7题:JSON 和 XML 互转(完整转换器)

实现一个 xml_to_json 函数,将任意 XML 字符串转换为 JSON,支持属性、文本、嵌套元素,列表处理。再实现反向 json_to_xml 转换。


🧠 知识点思维导图总结

第37课:JSON/CSV/XML

JSON

数据类型映射

函数: dumps/loads, dump/load

自定义编码 default/object_hook

参数: indent, ensure_ascii

Web API 交互

CSV

读写: reader/writer, DictReader/DictWriter

参数: delimiter, quotechar, quoting

处理特殊字符

与 Excel 兼容

XML

ElementTree 结构

解析: parse/fromstring

查找: find/findall/iter

修改和生成

XPath 基本表达式

大文件 iterparse

实用场景

配置管理

数据导出导入

API 数据处理

格式转换

面试考点

JSON 与 dict 区别

CSV 空白/引号问题

XML 与 JSON 优劣

大文件处理策略


🔜 下节课预告

掌握了常见数据格式的读写后,我们需要将这些数据存储到关系型数据库中。下一节课我们将学习 SQLite/MySQL 数据库基础,以及如何使用 Python 操作数据库。

第38课:数据库基础:MySQL安装配置与SQL语句零基础入门

内容包括:

  • 关系数据库和 SQL 基础(DDL/DML/DQL)
  • MySQL 安装与基本配置(或使用 SQLite 免安装)
  • Python 连接数据库(mysql-connector-pythonsqlite3
  • 执行 SQL 语句、事务处理
  • 实战:创建表、增删改查

数据库是后端开发的必备技能,掌握它你将能构建数据驱动的应用。

🌟 学习鼓励:JSON、CSV、XML 是日常开发中频繁打交道的数据格式。通过本课的学习,你已经具备处理外部数据的能力。请动手完成练习,特别是 JSON 与 CSV 互转、XML 解析等任务,这些是实际项目中的高频操作。下一阶段我们将进入数据库编程,继续保持热情!


🔗《50节课 Python 从入门到精通》系列课程导航

去订阅

🌟 感谢您耐心阅读到这里!
💡 如果本文对您有所启发欢迎:
👍 点赞📌 收藏 📤 分享给更多需要的伙伴。
🗣️ 期待在评论区看到您的想法, 共同进步。
🔔 关注我,持续获取更多干货内容~
🤗 我们下篇文章见~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thomas.Sir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值