Excel 文件读写
本教程共 54 篇 · 第 12 篇 · 更新于 2026-08-11 · 约 8 分钟阅读
本节目标:掌握 read_excel 和 to_excel 的用法,能按 sheet 读取数据、把多个 DataFrame 写进同一个工作簿,并处理 Unnamed 列等常见格式问题。
先装依赖:openpyxl
Excel 是二进制格式,pandas 自己解析不了,需要第三方引擎。读写 .xlsx(Excel 2007+)最常用的是 openpyxl:
pip install openpyxl
常用的引擎对应关系:
openpyxl:读写.xlsx/.xlsm,最常用xlrd:读老格式.xlsxlsxwriter:写.xlsx,格式功能更强(可选)odf:读写 OpenDocument(.ods)python-calamine:一个 Rust 写的快速引擎,能读多种格式
不指定 engine 时,pandas 会根据文件扩展名自动选。只要装了 openpyxl,日常的 .xlsx 读写就没问题。
Warning没装 openpyxl 就调 read_excel,会报
ImportError并提示你安装。这不是 pandas 的问题,是缺可选依赖。
读取:read_excel
基本用法和 read_csv 很接近,多了一个 sheet 的概念:
import pandas as pd
df = pd.read_excel("数据.xlsx") # 默认读第一个 sheet
df = pd.read_excel("数据.xlsx", sheet_name="员工表") # 按名字读
df = pd.read_excel("数据.xlsx", sheet_name=1) # 按下标读,0 起
sheet_name 的取值规则:
- 整数:第几个 sheet(从 0 开始)
- 字符串:sheet 的名字,如
"Sheet1" - 列表:读多个 sheet,返回字典,键是 sheet 名
None:读全部 sheet,同样返回字典
dfs = pd.read_excel("数据.xlsx", sheet_name=["员工表", "工资表"])
print(type(dfs)) # dict
print(dfs["员工表"]) # 每个 sheet 一个 DataFrame
其他参数和 CSV 基本一致,挑常用的看:
df = pd.read_excel("数据.xlsx",
sheet_name="员工表",
header=0, # 第一行是表头
index_col="工号", # 用"工号"列做索引
usecols="A:D", # 只读 A 到 D 列
dtype={"年龄": "int64"}, # 指定列类型
na_values=["未知"], # 自定义缺失标记
parse_dates=["入职日期"]) # 解析日期列
几点说明:
usecols可以写 Excel 风格的"A:C",也可以传列名列表["姓名", "年龄"]parse_dates把看起来像日期的字符串列转成datetime64- 日期列在 Excel 里本身就是日期格式时,pandas 会自动识别,不需要额外配置
- 想跳过开头几行说明文字,用
skiprows
读取时的常见坑:Unnamed 列
Excel 表格不像 CSV 那么”规矩”,第一行经常不是表头而是标题。比如某个文件第一行写着”表格 1”,第二行才是 Name、Age、City。直接读,会出现 Unnamed: 1 这种奇怪的列:
表格 1 Unnamed: 1 Unnamed: 2
0 Name Age City
1 Alice 25 New York
pandas 把第一行当成了表头,真实数据全被挤到了数据区。对策是跳过标题行、指定自己的列名:
df = pd.read_excel("data.xlsx", header=None, skiprows=1,
names=["Name", "Age", "City"])
print(df)
另一个坑是合并单元格:合并的区域只有左上角有值,其余位置读进来是 NaN。处理思路是先 fillna 再向下填充(缺失值处理在 §25 细讲),或者让 Excel 端别合并。
多 sheet 的高效姿势:ExcelFile
用 read_excel 连续读多个 sheet,文件会被反复解析。如果只读一次文件、多次取 sheet,用 ExcelFile 更高效:
with pd.ExcelFile("数据.xlsx") as xls:
print(xls.sheet_names) # 所有 sheet 名
df1 = pd.read_excel(xls, "员工表")
df2 = pd.read_excel(xls, "工资表")
ExcelFile 把文件读进内存一次,之后按名字或下标取 sheet 都不再碰磁盘。它还能配合 with 自动释放资源。
如果所有 sheet 的解析参数完全一样,直接传列表给 read_excel 也不会有性能损失:
data = pd.read_excel("数据.xlsx", ["员工表", "工资表"],
index_col=None, na_values=["NA"])
Tip一个工作簿几十个 sheet、参数各不相同(有的要跳过前两行,有的要指定索引列),这是
ExcelFile的主场。先用xls.sheet_names看看有哪些表,再逐张配置读取。
写出:to_excel
to_excel 负责把 DataFrame 写成 Excel:
df.to_excel("输出.xlsx") # 默认写到 Sheet1
df.to_excel("输出.xlsx", sheet_name="员工表") # 指定 sheet 名
df.to_excel("输出.xlsx", index=False) # 不写行索引
和 CSV 一样,index=False 是最常用的参数——默认会把行索引写成第一列。
控制输出格式的参数:
df.to_excel("输出.xlsx",
sheet_name="员工表",
index=False,
index_label="编号", # 索引列的表头名
float_format="%.2f", # 浮点数保留两位
freeze_panes=(1, 0), # 冻结第一行
autofilter=True) # 开启筛选按钮
float_format:浮点数的显示格式freeze_panes:冻结窗格,(1, 0)冻结第一行,滚动时表头不动autofilter:给每个列加上自动筛选下拉框merge_cells=False:配合index_label时,让索引标签放在第一行而不是第二行
多表写入:ExcelWriter
想在一个工作簿里放多个 DataFrame,用 ExcelWriter:
df1 = pd.DataFrame({"姓名": ["张三", "李四"], "分数": [85, 92]})
df2 = pd.DataFrame({"产品": ["键盘", "鼠标"], "价格": [99, 45]})
with pd.ExcelWriter("输出.xlsx") as writer:
df1.to_excel(writer, sheet_name="成绩表", index=False)
df2.to_excel(writer, sheet_name="价格表", index=False)
with 块结束时自动保存并关闭文件。往已有的工作簿追加 sheet,用 mode="a":
with pd.ExcelWriter("输出.xlsx", mode="a", engine="openpyxl") as writer:
df3.to_excel(writer, sheet_name="新表", index=False)
追加模式下 engine 必须指定为 openpyxl。同名 sheet 的处理由 if_sheet_exists 控制:默认 "error" 报错,可以设 "replace" 覆盖、"new" 自动改名、"overlay" 在原位置覆盖写入。
Warning
mode="a"只对 openpyxl 引擎有效。想往既有文件追加 sheet,务必带上engine="openpyxl",否则会报错。
Excel 不进磁盘:写入内存缓冲区
Excel 文件不一定要落盘。用 BytesIO 可以把工作簿直接生成在内存里——网站做”导出 Excel 下载”功能时常用,文件不经过磁盘,直接返回给前端:
from io import BytesIO
bio = BytesIO()
with pd.ExcelWriter(bio) as writer:
df.to_excel(writer, sheet_name="Sheet1", index=False)
bio.seek(0) # 回到开头才能读
data = bio.read() # data 就是完整的 xlsx 字节
注意两个细节:写完后必须先 seek(0) 再读;ExcelWriter 用完会关闭,别在 with 块外继续写。
大文件读取加速
openpyxl 解析大 Excel 偏慢。装 python-calamine 后可以换用 calamine 引擎,它是 Rust 实现,通常比 openpyxl 快不少:
df = pd.read_excel("很大.xlsx", engine="calamine")
calamine 还能读 .xls、.xlsb、.ods 等多种格式。写入暂时还是用 openpyxl / xlsxwriter,calamine 只管读。
格式与样式
to_excel 用 openpyxl 引擎写出的工作簿默认不带任何样式——没有加粗表头、没有边框(换成 xlsxwriter 引擎会带默认表头格式)。想要花哨的样式,用 Styler:
css = "border: 1px solid black; font-weight: bold;"
df.style.map_index(lambda x: css).map_index(lambda x: css, axis=1).to_excel("带样式.xlsx")
如果你装了 xlsxwriter 引擎,to_excel(engine="xlsxwriter") 还能做更多精细排版(列宽、条件格式、图表等),有兴趣可以查 xlsxwriter 的 pandas 文档。
小结
- 读写
.xlsx需要openpyxl,其他格式对应其他引擎 sheet_name用整数、名字、列表或None控制读取范围,多 sheet 返回字典- 标题行、合并单元格会让列名变成
Unnamed,用header=None+skiprows+names解决 - 多次读同一个文件用
ExcelFile,一次解析、按需取表 to_excel记得index=False;freeze_panes、autofilter控制观感- 多表写一个文件用
ExcelWriter,追加模式mode="a"+engine="openpyxl" - 导出下载用
BytesIO内存缓冲;大文件读取换engine="calamine"加速 - 3.0 默认无样式,要样式用
Styler.to_excel或 xlsxwriter
下一节离开表格文件,看 JSON、HTML、XML 和剪贴板这些”非表格”格式怎么读。