Python操作.xls和.xlsx格式该分别用什么库
详细讲解Python操作.xls和.xlsx两种Excel格式应分别选用什么库,深入分析底层格式差异、读写与修改场景下的工具选择、代码示例及常见报错排查方法。
在Python中处理Excel文件,核心原则是根据文件扩展名对应的底层格式分别选库:操作新版的 .xlsx 格式,读写与修改首选 openpyxl,只生成大体量或复杂样式报表可选 xlsxwriter;操作旧版的 .xls 格式,读取使用 xlrd,新建写入使用 xlwt,在已有文件上追加或修改则结合 xlutils。如果主要任务是批量数据分析或跨格式转换,可以使用 pandas 作为统一上层接口,但底层依然需要安装上述对应的引擎库。下面从格式差异、选库判断标准、具体代码操作到常见报错排查逐一展开说明。
一、为什么.xls和.xlsx不能混用同一个底层库
许多初学者在用Python读取Excel时会遇到格式不支持的报错,直接原因在于 .xls 和 .xlsx 虽然都显示为Excel表格,但内部存储结构完全不同,没有任何一套纯Python底层解析器会同时实现这两种格式的完整读写逻辑。
.xls 是Excel 97-2003时代使用的二进制格式(基于BIFF8规范),所有单元格数据、样式和工作表结构都以紧密的二进制流记录在复合文档中,单张工作表最多只能容纳65536行、256列。而 .xlsx 是Excel 2007之后采用的开发办公文档格式(OOXML标准),本质上是一个ZIP压缩包,解压后可以看到多个记录工作表数据、共享字符串和样式的XML文件,单表支持1048576行、16384列。
更关键的变化发生在 xlrd 库的2.0.0版本之后。早期 xlrd 曾同时支持读取 .xls 和 .xlsx,但由于解析XML存在潜在安全隐患且维护重心重叠,官方从2.0.0版本起彻底移除了对 .xlsx 的支持。因此,现在必须严格区分文件格式来搭配Python库。
二、按具体需求选择对应库的判断方法
在实际项目中,不仅要看文件后缀是 .xls 还是 .xlsx,还要区分你是“只读数据”“从零生成新表”还是“保留原表格式并修改部分单元格”。不同库在读写权限和样式保留能力上有明确边界。
总结起来,选择依据可以归纳为以下三条明确规则:
- 处理
.xlsx文件(当前主流场景):只要涉及读取、修改已有单元格、调整字体边框或插入行列,一律使用openpyxl。只有在“不需要读取旧文件,仅从零生成包含大量图表、条件格式或几十万行数据的新.xlsx文件”时,才优先选用内存控制和图表支持更好的xlsxwriter。 - 处理
.xls文件(兼容历史系统或老旧导出文件):只读提取数据使用xlrd;从零生成.xls文件使用xlwt;必须在原有.xls文件上修改数据并保存,需要同时安装xlrd、xlwt和xlutils。 - 以数据清洗、统计汇总为核心:不管源文件是
.xls还是.xlsx,不需要精细控制合并单元格或颜色边框时,直接使用pandas,并在环境中安装好openpyxl和xlrd作为其底层引擎。
三、操作.xlsx格式:使用openpyxl完成读写与修改
openpyxl 是目前Python处理 .xlsx(以及启用宏的 .xlsm)最核心的库。它的工作对象依次是工作簿(Workbook)、工作表(Worksheet)和单元格(Cell)。需要注意的是,openpyxl 中所有行号和列号都按照Excel的习惯从 1 开始计数,而不是从 0 开始。
1. 安装环境准备
在命令行终端中执行以下pip命令安装 openpyxl:
pip install openpyxl
2. 新建、读取与追加修改.xlsx完整示例
下面的代码演示了在同一目录下先创建一份名为 sales_2026.xlsx 的文件,随后读取其中的数据,并在末尾追加一行新记录后保存。在项目工作目录中新建 handle_xlsx.py 并运行:
from openpyxl import Workbook, load_workbook
from openpyxl.styles import Font
file_path = "sales_2026.xlsx"
# 第一步:新建.xlsx工作簿并写入初始数据
wb = Workbook()
ws = wb.active
ws.title = "月度销量"
# 写入表头并设置加粗字体
ws.append(["月份", "产品名称", "销量", "单价"])
for col_idx in range(1, 5):
ws.cell(row=1, column=col_idx).font = Font(bold=True)
# 写入两行示例数据
ws.append(["2026-01", "无线键盘", 120, 159.0])
ws.append(["2026-02", "机械鼠标", 95, 199.0])
wb.save(file_path)
print("1. 已创建文件:", file_path)
# 第二步:读取已有的.xlsx文件
# data_only=True 表示若单元格含公式则读取最后一次计算的数值结果
wb_read = load_workbook(file_path, data_only=True)
sheet = wb_read["月度销量"]
print("2. 读取到的表格内容:")
for row in sheet.iter_rows(min_row=1, values_only=True):
print(" ", row)
# 第三步:在原表上修改单元格并追加新行
wb_edit = load_workbook(file_path)
edit_sheet = wb_edit["月度销量"]
# 修改第2行第3列(即2026-01的销量)为135
edit_sheet.cell(row=2, column=3, value=135)
# 在末尾追加一行
edit_sheet.append(["2026-03", "高清摄像头", 80, 249.0])
wb_edit.save(file_path)
print("3. 已完成修改与追加,当前总行数:", edit_sheet.max_row)
load_workbook(file_path, data_only=True) 打开文件后,千万不要直接调用 save() 覆盖原文件。因为 data_only=True 会把所有公式替换为纯数值,一旦保存,原表格里的公式就会永久丢失。需要修改表格时应使用默认的 load_workbook(file_path)。
四、操作.xls格式:使用xlrd、xlwt与xlutils
面对政府申报系统、早期ERP或银行对账单导出的 .xls 文件,不能使用 openpyxl 打开。此时需要安装专门针对旧版二进制格式的工具组合。与 openpyxl 不同,xlrd 和 xlwt 的行号、列号索引全部从 0 开始计数。
1. 安装环境准备
在命令行终端中一次性安装读取、写入和修改所需的三个库:
pip install xlrd xlwt xlutils
2. 新建、读取与修改.xls完整示例
由于 xlwt 只能新建文件,无法直接打开已有 .xls 进行修改,如果需要修改已有 .xls 文件,必须先用 xlrd 读取工作簿,再通过 xlutils.copy.copy() 将其复制到内存中的可写对象,修改后再保存。新建 handle_xls.py 并运行:
import xlrd
import xlwt
from xlutils.copy import copy as xl_copy
xls_path = "legacy_inventory.xls"
# 第一步:使用 xlwt 新建 .xls 文件(行列索引从 0 开始)
wb_new = xlwt.Workbook(encoding="utf-8")
ws_new = wb_new.add_sheet("库存清单")
headers = ["编号", "配件名称", "库存数量"]
for col_idx, text in enumerate(headers):
ws_new.write(0, col_idx, text)
ws_new.write(1, 0, "A001")
ws_new.write(1, 1, "散热风扇")
ws_new.write(1, 2, 50)
wb_new.save(xls_path)
print("1. 已生成旧版文件:", xls_path)
# 第二步:使用 xlrd 读取 .xls 文件
# formatting_info=True 用于保留原有单元格样式信息,供后续 xlutils 复制使用
rb = xlrd.open_workbook(xls_path, formatting_info=True)
r_sheet = rb.sheet_by_name("库存清单")
print("2. 读取到的行数:", r_sheet.nrows, "列数:", r_sheet.ncols)
for row_idx in range(r_sheet.nrows):
print(" ", r_sheet.row_values(row_idx))
# 第三步:使用 xlutils.copy 在原文件基础上修改并追加数据
wb_modify = xl_copy(rb)
m_sheet = wb_modify.get_sheet(0)
# 将第2行(索引1)第3列(索引2)的库存数量从 50 改为 65
m_sheet.write(1, 2, 65)
# 在第3行(索引2)追加一条新记录
m_sheet.write(2, 0, "A002")
m_sheet.write(2, 1, "电源模块")
m_sheet.write(2, 2, 30)
wb_modify.save(xls_path)
print("3. 已完成对 legacy_inventory.xls 的更新保存")
细心的读者会发现,示例输出中整数 50 被 xlrd 读取成了浮点数 50.0。这是因为在旧版 .xls 的底层二进制存储中,普通整数和浮点数统一按双精度浮点数存储。如果需要还原为整数,可以在读取时检查单元格类型或用 int(val) 转换。
五、用pandas统一读取及将.xls安全转换为.xlsx
如果你不需要逐个调整单元格样式,只是想把表格读进Python做筛选、合并、分组统计,或者想把历史遗留的 .xls 批量转换成 .xlsx,使用 pandas 是最省力的做法。
pandas 通过 engine 参数指定底层解析库:读取 .xls 时指定 engine="xlrd",读写 .xlsx 时指定 engine="openpyxl"。此外,实际工作中经常遇到“文件后缀名叫 .xls,但内部其实是网页HTML表格、CSV文本或者重命名过的 .xlsx”的情况。直接按后缀调用库会立刻报错,因此在批量转换前,可以先读取文件前几个字节(文件头魔数)核对真实格式。
完整代码:检测文件真实格式并转换.xls到.xlsx
先确保已安装 pandas(以及前面安装过的 xlrd 和 openpyxl):
pip install pandas openpyxl xlrd
在相同目录下创建 convert_excel.py,该脚本会先检查前面生成的 legacy_inventory.xls 的文件头字节,确认是真实的二进制 .xls 后,将其全部工作表无损导出为 modern_inventory.xlsx:
import pandas as pd
def detect_excel_format(file_path):
"""通过文件前8个字节判断Excel文件的真实底层格式"""
with open(file_path, "rb") as f:
header = f.read(8)
if header.startswith(b"\xd0\xcf\x11\xe0"):
return "xls"
elif header.startswith(b"PK\x03\x04"):
return "xlsx"
elif header.lstrip().startswith((b"<html", b"<HTML", b"<table", b"<TABLE")):
return "html"
return "unknown"
src_file = "legacy_inventory.xls"
dst_file = "modern_inventory.xlsx"
real_fmt = detect_excel_format(src_file)
print(f"检测到 {src_file} 的真实底层格式为: {real_fmt}")
if real_fmt == "xls":
# sheet_name=None 表示读取所有工作表,返回 {表名: DataFrame} 字典
all_sheets = pd.read_excel(src_file, sheet_name=None, engine="xlrd")
with pd.ExcelWriter(dst_file, engine="openpyxl") as writer:
for sheet_name, df in all_sheets.items():
df.to_excel(writer, sheet_name=sheet_name, index=False)
print(f"已将全部工作表转换并保存至: {dst_file}")
# 验证转换后的.xlsx文件
df_verify = pd.read_excel(dst_file, sheet_name="库存清单", engine="openpyxl")
print("转换后读取验证:")
print(df_verify)
六、常见报错原因与排查方法
在混用 .xls 和 .xlsx 的过程中,最常遇到以下三类报错。根据报错提示中的关键词,可以迅速定位是选错了库还是文件本身名不副实:
1. 报错:xlrd.biffh.XLRDError: Excel xlsx file; not supported
- 触发原因:使用了 2.0.0 及以上版本的
xlrd去读取.xlsx格式的文件(或者在pandas.read_excel()中对.xlsx强行指定了engine="xlrd")。 - 解决方法:不要通过降级安装旧版
xlrd来规避问题。正确的做法是将代码改用openpyxl,或在pandas.read_excel()中设置engine="openpyxl"。
2. 报错:openpyxl.utils.exceptions.InvalidFileException 或 zipfile.BadZipFile: File is not a zip file
- 触发原因:用
openpyxl打开了不支持的文件。常见于两种情况:一是直接把.xls文件传给了openpyxl;二是有人把.xls、.csv或网页导出的表格直接手动重命名改成了.xlsx后缀,导致它根本不是合法的ZIP压缩包。 - 解决方法:运行上一节中的
detect_excel_format()函数检查文件头。若返回xls,改用xlrd读取;若返回html,说明是网页伪装的Excel,可使用pandas.read_html()解析;若是空文件(0字节),则需检查上游文件生成逻辑是否未正常调用save()。
3. 报错:ValueError: row index was 65536, not allowed by .xls format
- 触发原因:使用
xlwt往.xls文件写入数据时,行数超过了65536行的格式上限。 - 解决方法:这不是
xlwt库的缺陷,而是.xls二进制规范本身的硬性限制。当数据量可能超过6万行时,必须改用openpyxl(开启write_only=True只写模式)或xlsxwriter导出为.xlsx格式。
七、各库功能边界与选择总结
为了在日常开发中快速查阅,下表与总结图整理了Python操作 .xls 和 .xlsx 各主流库在读写权限、格式支持和典型用途上的核心差异:
| 库名称 | 支持格式 | 读取 | 新建写入 | 修改已有文件 | 样式与公式控制 | 最适合的使用场景 |
|---|---|---|---|---|---|---|
openpyxl |
.xlsx / .xlsm |
支持 | 支持 | 支持 | 全面支持 | 日常读写、修改 .xlsx 单元格及样式的首选标准库 |
xlsxwriter |
.xlsx |
不支持 | 支持 | 不支持 | 极强(仅写入) | 从零生成含复杂图表、条件格式或超大行数的 .xlsx 报表 |
xlrd |
.xls(2.0+) |
支持 | 不支持 | 不支持 | 仅基础格式读取 | 读取历史遗留或外部系统导出的旧版 .xls 文件 |
xlwt |
.xls |
不支持 | 支持 | 不支持 | 支持基础样式 | 应旧系统要求必须生成 65536 行以内的 .xls 文件 |
xlutils |
.xls |
需配 xlrd |
需配 xlwt |
支持(复制模式) | 有限保留 | 在已有 .xls 文件基础上补充或修改少量单元格 |
pandas |
.xls / .xlsx |
支持(配引擎) | 支持(配引擎) | 可追加工作表 | 弱(偏重纯数据) | 批量数据清洗、多表聚合统计及 .xls 转 .xlsx |
掌握上述对应关系后,遇到任何Excel处理任务时,只需先看清文件是 .xls 还是 .xlsx(必要时检验文件头),再按“.xlsx 主用 openpyxl、.xls 读写分用 xlrd/xlwt、批量分析用 pandas”的组合配置环境,就能稳定完成开发而不再被版本兼容问题困扰。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









