首页 / Pandas 入门教程 / JSON 与其他文本格式读写

Pandas 入门教程

JSON 与其他文本格式读写

本教程共 54 篇 · 第 13 篇 · 更新于 2026-08-11 · 约 8 分钟阅读

PandasPandas 入门教程read_jsonjson_normalizeread_htmlread_xml

本节目标:学会读写 JSON(重点搞懂 orient 方向)、展平嵌套 JSON,并了解 HTML 表格、XML 和剪贴板这几种”非表格”数据的读取方法。

JSON:API 世界的通用语

JSON(JavaScript Object Notation)是一种轻量文本格式,网页接口(API)返回的数据大多是它。它的基本结构只有两种:对象(花括号,键值对)和数组(方括号,列表)。

pandas 读写 JSON 用 read_jsonto_json,难点不在函数本身,而在 JSON 的”长相”千变万化——同样的表格数据,可以用好几种结构表达。这就是 orient 参数存在的意义。

读取:read_json

从文件、URL 或字符串读取:

import pandas as pd
from io import StringIO

df = pd.read_json("数据.json")
df = pd.read_json("https://example.com/api/data.json")
df = pd.read_json(StringIO('{"a": {"0": 1, "1": 2}}'))

不指定 orient 时,pandas 按默认方向(columns)解析,期望 JSON 是”外层列名、内层行号”的结构。最常见的情况:数据源是数组套对象(每行一个对象),这是 web API 的标配:

json_str = '[{"姓名": "张三", "年龄": 25}, {"姓名": "李四", "年龄": 30}]'
df = pd.read_json(StringIO(json_str), orient="records")
print(df)
Note

默认方向也能解析这种数组,但显式写 orient="records" 意图更清楚,也能避免某些边界情况的误判。写成什么方向,读的时候就指定什么方向。

orient:六种方向一次看清

to_json 默认输出 columns 方向,read_json 的默认方向与之对应。拿这个 DataFrame 举例:

df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]},
                  index=["x", "y", "z"])

各方向输出的结构:

print(df.to_json(orient="columns"))  # 列在外:{"A":{"x":1,...},"B":{"x":4,...}}
print(df.to_json(orient="index"))    # 行在外:{"x":{"A":1,"B":4},...}
print(df.to_json(orient="records"))  # 数组套对象:[{"A":1,"B":4},...]
print(df.to_json(orient="split"))    # 拆三份:{"columns":[...],"index":[...],"data":[...]}
print(df.to_json(orient="values"))   # 纯数组:[[1,4],[2,5],[3,6]]

逐个说适用场景:

  • columns:默认方向,列名做外层键,适合 pandas 之间的往返
  • index:行标签做外层键,和 columns 正好反着
  • records:数组套对象,最贴近 web API 习惯,前端 JS 直接能用
  • split:索引、列名、数据分开存,信息最完整,往返无损
  • values:只要数据,省掉所有标签,最省体积

还有一个 table 方向,按 JSON Table Schema 格式输出,会带上 dtype 等元数据,适合完整存档。

Warning

往返读取时 orient 必须一致:to_json(orient="records") 写出的文件,read_json(..., orient="records") 才能正确读回。方向不匹配时数据会错位或报错。

写出:to_json

to_json 不传路径时返回 JSON 字符串,传路径则写文件:

s = df.to_json()                       # 返回字符串
df.to_json("输出.json", orient="records")   # 写文件

# 含日期的数据
df2 = pd.DataFrame({"日期": pd.to_datetime(["2026-01-01", "2026-01-02"])})
print(df2.to_json(date_format="iso"))  # 日期写成 ISO 格式

date_format="iso" 把日期写成 2026-01-01T00:00:00.000 这种可读格式;默认 "epoch" 是时间戳数字。跨系统交换数据时 iso 更保险。

JSONL:一行一个 JSON

JSONL(JSON Lines)格式每行是一个独立的 JSON 对象,日志文件、流式数据常用:

jsonl = '{"a": 1, "b": 2}\n{"a": 3, "b": 4}\n'
df = pd.read_json(StringIO(jsonl), lines=True)
print(df)

df.to_json("输出.jsonl", orient="records", lines=True)

读写都要配 orient="records" + lines=True。大文件还能配 chunksize 分块读:

with pd.read_json("大文件.jsonl", lines=True, chunksize=1000) as reader:
    for chunk in reader:
        print(chunk.shape)   # 每次只读 1000 行

嵌套 JSON:json_normalize 展平

真实世界的 JSON 常常是嵌套的——对象套对象、对象套数组。read_json 只能解析扁平结构,嵌套的要用 json_normalize 展平。

先看对象嵌套:

data = [
    {"id": 1, "name": {"first": "Coleen", "last": "Volk"}},
    {"id": 2, "name": {"first": "Mark", "last": "Regner"}},
]
df = pd.json_normalize(data)
print(df)   # name 展开成 name.first 和 name.last 两列

再看数组嵌套(学生列表挂在班级对象下),用 record_path 指定要展开的字段,meta 保留外围字段:

data = {
    "school_name": "ABC 小学",
    "class": "一年级",
    "students": [
        {"id": "A001", "name": "Tom", "math": 60},
        {"id": "A002", "name": "James", "math": 89},
    ],
}
df = pd.json_normalize(data, record_path="students",
                       meta=["school_name", "class"])
print(df)   # 每个学生一行,班级信息重复填充

max_level 参数控制嵌套展开的深度,默认全展开。

Tip

实战口诀:扁平 JSON 用 read_json,嵌套 JSON 先 json.loads 解析成 Python 对象,再交给 pd.json_normalize

类型推断的小脾气

read_json 自动转类型时有几个”怪癖”,读回数据时要留意:

  • 全是整数值的浮点列会被转成整数(1. 变成 1
  • 布尔列可能被转成整数(True 变成 1
  • 索引重建后的顺序不保证和序列化时一致

想保持原样,用 dtype 显式指定:

df = pd.read_json(StringIO(json_str), dtype={"price": "float64"})

重要数据的往返,建议显式配 dtype,别依赖自动推断。

HTML 表格:read_html

网页里的 <table> 标签可以直接变成 DataFrame:

tables = pd.read_html("https://example.com/table.html")
df = tables[0]   # 返回列表,网页有几个表格就有几个元素

常用筛选参数:

tables = pd.read_html("page.html",
                      match="销售额",          # 只留包含该文字的表格
                      attrs={"id": "data-table"},  # 按 HTML 属性定位
                      header=0,               # 指定表头行
                      index_col=0)            # 指定索引列

反向操作是 to_html,把 DataFrame 渲染成 HTML 表格字符串,方便嵌入网页或邮件:

html = df.to_html()

read_html 需要解析器:lxml,或 beautifulsoup4 + html5lib,用前先 pip install lxml beautifulsoup4。另外请遵守目标网站的 robots.txt,别高频抓取。

XML:read_xml

XML 和 HTML 同源,read_xml 把它解析成 DataFrame:

xml = """<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
  <book category="cooking">
    <title>Everyday Italian</title>
    <author>Giada</author>
    <year>2005</year>
  </book>
  <book category="children">
    <title>Harry Potter</title>
    <author>Rowling</author>
    <year>2005</year>
  </book>
</bookstore>"""

df = pd.read_xml(StringIO(xml))
print(df)   # 每个 book 节点一行,子元素变列,属性也变列

XML 的数据可能藏在属性里(category="cooking"),也可能在子元素里。默认两者都读成列;只想取一种可以这样:

df = pd.read_xml(StringIO(xml), elems_only=True)   # 只要子元素
df = pd.read_xml(StringIO(xml), attrs_only=True)   # 只要属性

复杂文档用 xpath 选节点,比如 xpath="//book[year=2005]"read_xml 需要 lxml 库,写 XML 用 to_xml

剪贴板:read_clipboard

最后来一个轻量技巧:剪贴板。在 Excel 里复制一块区域,直接变成 DataFrame:

df = pd.read_clipboard()   # 粘贴剪贴板内容

反向操作,把 DataFrame 复制到剪贴板,去 Excel 里粘贴:

df.to_clipboard()

处理临时数据、在 Excel 和 pandas 之间搬运小数据时,比存文件快得多。Linux 上需要 xclip 或 xsel 支持。

小结

  • read_json / to_json 的核心是 orient:records 贴近 API,split 往返无损,columns 是默认
  • 读写方向必须一致;日期用 date_format="iso"
  • JSONL 用 lines=True,大文件配 chunksize 分块
  • 嵌套 JSON 用 pd.json_normalizerecord_path 展数组、meta 保留外围字段
  • 读回 JSON 注意类型怪癖:浮点转整数、布尔转整数,用 dtype 固定
  • HTML 表格 read_html 返回列表,XML 用 read_xml,都要装解析库
  • read_clipboard / to_clipboard 在 Excel 和 pandas 之间零成本搬运

下一节把数据写进数据库:SQL 读写。