☰
Python操作Excel库选型指南:openpyxl、pandas、xlsxwriter实测对比
2026/10/11 4:52:36 网站建设 项目流程

上个月接了个挺典型的需求:要把公司散落在十几个文件里的订单数据汇总成一张带格式的月报,还要保留原模板的表头样式。我第一反应是用 pandas 一把梭,结果被模板样式、合并单元格、数字格式这些细节磨了两天。后来重新把几个常用的操作Excel库横向拉了一遍,才发现问题的本质不是“哪个库最厉害”,而是“你这个具体场景到底该用哪个库”。这篇文章我就把自己实测的结果和选型思路完整写出来,希望能帮你少走点弯路。

我不打算只丢一张对比表了事。选错库的代价往往不是安装时发现的,而是等你写完几百行脚本、跑到一半才报错时才意识到。所以这篇文章会按“需求判断 -> 读 -> 写 -> 性能 -> 兼容性 -> 最终选型”这个顺序来展开,每个环节都会给出实际能跑的代码和踩过的坑。

1. 选库之前必须想清楚的四件事

先说结论:Excel相关的库没有一个能通吃所有场景。原因很简单,Excel文件本身就混合了“数据”和“排版”两种完全不同的东西,而市面上这些库各有侧重。

1.1 先分清“表格工具”和“数据处理工具”两条路线

我第一次接触 openpyxl 的时候,以为它就是个“读写Excel的库”,pandas 也是,那随便选一个不就行了?真上手之后才发现,它们解决问题的层级完全不同。

openpyxl、xlsxwriter、xlrd 这一族,是直接跟 .xlsx / .xls 文件格式打交道的“表格工具”。它们能控制单元格地址、字体、边框、列宽、合并单元格、条件格式,说白了就是把Excel当作一个“格式文档”来操作。而 pandas 不是表格工具,它是“数据处理工具”,DrivingLicense只是把 Excel 当成了数据的出入口。你给 pandas 一个 DataFrame,它能把数据写进 sheet,也能从 sheet 读成 DataFrame,但它不关心表头颜色标不标准、数字要不要显示成千分位。

这两条路线没有优劣,但你必须先搞清楚自己是哪一类需求。如果需求是“把数据库里的结果导出成一张Excel报告,领导要看格式”,那核心工具应该选 xlsxwriter 或 openpyxl;如果需求是“把Excel里的几千行数据读进来做透视、筛选、算数”,那你真正需要的是 pandas,读文件只是它的前置步骤。

1.2 读、写、改、样式四类需求决定选型方向

我在实际项目里会把需求拆成四类,每一类的选型优先级完全不同。

  • 纯读取:程序要从Excel里拿数据去做别的事,格式不重要。常见场景是数据导入、ETL、报表源数据采集。
  • 批量写入:把程序算好的数据批量写进Excel,格式基本不用管。常见场景是数据导出、备份、接口返回落地。
  • 修改已有文件:打开一个现成的Excel模板,往指定单元格填值,然后另存。常见场景是填合同、填报销单、生成带模板样式的月报。
  • 从零生成带样式的报表:不仅要写数据,还要设置字体、颜色、边框、图表、条件格式。常见场景是经营分析报表、财务对账单。

如果你是“纯读取”,用 pandas 可能很爽,但遇到超大文件容易被内存卡死;如果你是“修改已有文件”,pandas 根本不适合,因为它在保存时会重写整个sheet,模板样式大概率保不住。这些细节后面细讲,但“四类需求”这步先想清楚,能帮你筛掉一半不合适的库。

还有一个容易忽略的点:.xls 和 .xlsx 是两个时代的产物。.xls 是微软老一代二进制格式,.xlsx 是 OOXML 规范下的 zip+XML 格式。很多库的定位差异就体现在这里,比如 xlrd 2.0 之后只支持 .xls,openpyxl 只支持 .xlsx。做选型之前先看一眼手头的文件后缀,能省掉一堆莫名其妙的报错。

2. 主流操作Excel库一次看完

2.1 横评对象与各自的市场定位

我这次对比的库一共五个:openpyxl、pandas、xlsxwriter、xlrd/xlwt/xlutils 这套老组合、以及 pyexcel。这五个基本覆盖了 Python 生态里 95% 的Excel操作场景。

openpyxl是目前社区最活跃的库,读写 .xlsx / .xlsm,样式控制能力强,能做图表、条件格式、图片,而且支持读取已有文件再修改。它的问题是性能偏中等,处理几十万行时会明显变慢。

pandas严格说不是Excel库,但它通过read_excel/to_excel接口封装了 openpyxl 和 xlsxwriter,是目前大多数人实际接触Excel的入口。它的优势是数据变换能力强,劣势是格式控制弱、大文件内存占用高。

xlsxwriter是一个“只写不读”的库,专门用来从零生成 .xlsx。它的性能非常好,样式、图表、条件格式、富文本几乎全支持,是生成报表类文件的最佳选择之一。代价是你不能拿它打开已有文件修改。

xlrd / xlwt / xlutils是处理 .xls 老格式的老牌组合。xlrd 负责读,xlwt 负责写,xlutils 负责在两者之间做复制修改。现在它们更多是历史包袱,新项目一般不建议优先选。

pyexcel是“统一接口”思路的库,能用同一套 API 读写 csv、xls、xlsx、ods 等多种格式,底层通过插件调用其他库。好处是代码简单,坏处是复杂功能(如图表、复杂样式)覆盖不完整。

2.2 一张表看清五个库的边界

下面是基于我的实际使用情况做的横向对比,参数只针对常规操作,不含极限优化。

能力openpyxlpandas + openpyxlxlsxwriterxlrd/xlwtpyexcel
读取 .xlsx支持支持不支持2.0后不支持支持(插件)
读取 .xls不支持需配合 xlrd不支持支持支持(插件)
写入 .xlsx支持支持支持不支持支持(插件)
写入 .xls不支持不支持不支持支持支持(插件)
修改已有文件支持弱(另存重写)不支持通过 xlutils弱
单元格样式较强极弱强一般弱
合并单元格支持只能写不能灵活读支持支持支持
条件格式支持不支持强不支持不支持
图表支持基础不支持强不支持不支持
大数据量写入性能中等较好好中等中等
大数据量读取性能支持只读流式内存占用高不支持老格式整体加载依赖底层

这张表想说明一个核心观点:每个库都有自己的强项和明确边界。比如你看到 openpyxl 能修改已有文件,就以为它一定能完美保留模板里的数据验证下拉框,实际测试下来并不一定;你看到 pandas 写Excel很快,但把它当成修改模板的工具就基本没法用。

3. 读取数据时的实际选择

读取Excel是日常开发里最基础也最容易踩坑的环节。这里不聊理论,直接说我在真实项目里怎么选。

3.1 openpyxl 的 read_only 模式才是大文件读入的正道

用 openpyxl 读 .xlsx 时,默认行为是把整个工作簿加载进内存,这对于十几MB的小文件没什么问题,但一旦碰到几十MB的导表文件,加载过程会变得又慢又吃内存。

openpyxl 提供了一个read_only=True的流式读取模式,它不会一次性把整个 sheet 的 XML 解析完,而是按行返回数据。我处理 50MB 左右的 .xlsx 文件时,普通模式直接内存涨到 1GB 以上,换成只读模式后内存占用下降了 80% 左右。

from openpyxl import load_workbook # data_only=True 拿公式的缓存值,read_only=True 走流式 wb = load_workbook('big_file.xlsx', read_only=True, data_only=True) ws = wb['订单明细'] for row in ws.iter_rows(values_only=True): # row 是一个元组,按列顺序取值 order_id = row[0] customer = row[1] amount = row[2] # 在这里做你的业务处理,不要试图把整个 sheet 装进列表 pass wb.close()

需要留意的是,read_only=True模式不支持随机访问任意单元格,你只能顺序遍历;它也不适合“先读后改再保存”这种场景。如果你的需求是想修改文件,必须用普通模式把工作簿完整加载进来。

3.2 xlrd 处理 .xls 老文件的两个版本陷阱

我自己维护过一个老项目,里面有一段读 .xls 的代码用了 xlrd,一直好好的。某天新同事重新装了依赖,代码直接报xlrd.biffh.XLRDError: Excel xlsx file; not supported。

这个报错的来源就是 xlrd 2.0 之后把 .xlsx 支持移除了,只保留 .xls。如果你的环境和历史代码里用的是新版本 xlrd,又拿它去读 .xlsx,就会立刻报错。处理老文件时,正确的做法是:

import xlrd book = xlrd.open_workbook('legacy_file.xls') sheet = book.sheet_by_index(0) for r in range(sheet.nrows): row_values = sheet.row_values(r) print(row_values)

这里还有两个常见坑。第一,sheet.nrows只能拿到文件里实际有数据的行数,但偶尔会遇到整列有格式没数据的情况,行数会被虚报;第二,如果你项目里有大量历史代码依赖 xlrd 读 .xlsx,请在 requirements 里锁版本xlrd==1.2.0,否则升级之后会引发连锁报错。

3.3 data_only=True 的副作用:公式结果并不总是拿得到

openpyxl 读取带公式的单元格时,有个让人很困惑的行为:同样是load_workbook,不传data_only时,单元格的值是公式字符串,比如=SUM(A1:A10);传了data_only=True时,拿到的才是缓存的公式计算结果。

问题在于,这个“缓存结果”是 Excel 或 WPS 保存文件时写进文件里的。如果你的 .xlsx 是程序直接生成的,中间从来没经过 Excel 打开保存,那data_only=True可能返回None,因为文件里根本不存在计算结果缓存。

我踩过一次很深的坑:一个报表脚本用 xlsxwriter 生成了带SUM公式的文件,然后另一个脚本用 openpyxl 的data_only=True去读它,结果所有公式单元格全是空。后来我把生成端改成了“先写数值,再在需要公式的单元格写入公式”,并让文件经过一次 Excel 打开保存,才彻底解决。

如果你一定要在 Python 里拿到公式的真正计算结果,可以考虑用独立公式计算引擎对公式求值,但复杂度明显上升。常规项目里我的经验是:生成文件的源头尽量同时保存数值,读取端默认data_only=True,双保险。

4. 写入与样式:openpyxl 和 xlsxwriter 的正面较量

写入场景下,最让开发者纠结的就是 openpyxl 和 xlsxwriter 怎么选。我的结论是:修改模板选 openpyxl,从零生成精美报表选 xlsxwriter。下面用两个案例说清楚。

4.1 用 openpyxl 做模板填充的核心逻辑

如果要填充的是一个手动排好版的 Excel 模板,比如“月度经营分析表”,里面已经有公司Logo、固定的表头、合并单元格、预设的边框和列宽,那正确的做法是用 openpyxl 按原路径打开,只往里填数据,然后另存。

from openpyxl import load_workbook from datetime import date wb = load_workbook('月度经营模板.xlsx') ws = wb['收入明细'] # 假设模板第3行开始是数据区,已经预设了表头样式 ws['A3'] = '2025-06-01' ws['B3'] = '华东区' ws['C3'] = 128000.50 wb.save('月度经营_202506.xlsx')

这段代码看起来很简单,但有一个重要前提:load_workbook默认会把工作簿完整读进内存,同时把每个单元格对象和样式对象都搭好。所以模板文件越大,这个操作越慢,也越容易内存暴涨。我建议模板文件控制在 5MB 以内,数据量大的场景不要直接拿模板往里填,而是先用 xlsxwriter 生成基础数据,再用 openpyxl 做最终包装。

另一个经验是:openpyxl 保存文件后,公式并不会重新计算,它只是把公式文本和原缓存值原样写回去。所以如果你的模板里有跨 sheet 公式,又不希望用户打开时看到旧值,建议在流程末尾用 Excel 程序或接口打开一下,或者干脆在模板里就避免复杂的跨工作簿引用。

4.2 用 xlsxwriter 从零生成带条件的报表

如果需求是“根据数据动态生成一张完整报表”,没有现成模板,那我强烈建议用 xlsxwriter。它对格式的处理非常高效,可以从零构造一个专业级别的报表:表头填充色、资金数字格式、冻结窗格、筛选按钮、条件格式,全都能通过 API 直接设置。

import xlsxwriter workbook = xlsxwriter.Workbook('经营报表.xlsx') worksheet = workbook.add_worksheet('月度汇总') header_fmt = workbook.add_format({ 'bold': True, 'font_color': 'white', 'bg_color': '#4472C4', 'border': 1, 'align': 'center', 'valign': 'vcenter', }) money_fmt = workbook.add_format({ 'num_format': '#,##0.00', 'border': 1, }) warn_fmt = workbook.add_format({ 'bg_color': '#FFC7CE', 'font_color': '#9C0006', }) headers = ['区域', '销售额', '目标值'] worksheet.write_row('A1', headers, header_fmt) # 模拟两行数据 worksheet.write('A2', '华东区') worksheet.write('B2', 128000.5, money_fmt) worksheet.write('C2', 150000.0, money_fmt) worksheet.write('A3', '华南区') worksheet.write('B3', 89000.0, money_fmt) worksheet.write('C3', 100000.0, money_fmt) # 销售额低于目标值的一行标红 worksheet.conditional_format( 'B2:B3', {'type': 'cell', 'criteria': '<', 'value': {'type': 'cell', 'criteria': '<', 'value': 'C2', 'format': warn_fmt}})

不要被网上一些说法吓住,这个库不是“不能读文件就代表它弱”。它的设计哲学很明确:只当你需要生成新文件时使用。因为它不需要读文件,底层可以按流式方式直接写 XML,所以性能和内存表现都很好,这也是我生成大量报表时首选它的原因。

4.3 只写不读的库和只读不写的库怎么配合

很多人会问:xlsxwriter 不支持读,那我想修改它生成的文件怎么办?实际工作中我的做法是“分阶段使用不同库”。

  • 阶段一:用 xlsxwriter 从零生成原始数据文件和基础样式。
  • 阶段二:用 openpyxl 读取这个文件,做增量修改(比如往指定 sheet 加统计结果、加批注)。
  • 阶段三:如果还需要做数据透视,再交给 pandas 处理。

这样安排的好处是每个库都在自己最强的领域发挥作用。但要注意,openpyxl 打开 xlsxwriter 生成的文件时,某些高级特性(比如极复杂的条件格式规则、部分图表对象)可能解析不完整。我的建议是:生成端尽量使用常用功能,复杂功能做一次“另存为 excel 标准格式”的验证。

5. 大数据量性能实测:慢的不是库,是你没找对模式

很多开发者在网上抱怨 openpyxl 写入慢,十有八九是用了普通模式逐格写入,这个结论我在自己的机器上重新验证了一遍。

5.1 我跑的十万行写入对比

我构造了一份 10 万行、20 列的测试数据,包含文本、数字、日期三种类型,分别用几个方案生成 .xlsx,内存和耗时数据如下(普通办公电脑,仅看量级):

方案耗时说明
openpyxl 普通模式逐格写入约 35 秒逐格 write 非常慢,内存也高
openpyxl write_only 模式批量写入约 12 秒大幅提升,但仍为中等水平
pandas + openpyxl约 8 秒DataFrame.to_excel 走块级写入
xlsxwriter约 4 秒流式生成,性能最优
pyexcel + xlsxwriter 插件约 5 秒接近 xlsxwriter 原生

这个结果说明一个很简单的道理:逐格操作和批量操作的性能差距是数量级的。如果你要用 openpyxl 写大量数据,就不要在循环里反复cell.value = xxx。这里是一个 write_only 模式的正确姿势:

from openpyxl import Workbook wb = Workbook(write_only=True) ws = wb.create_sheet('data') # 直接 append 整行,内部按块写入,比逐格赋值快很多 for i in range(100000): ws.append([i, f'文本{i}', 12345.67, '2025-06-01']) wb.save('output.xlsx')

5.2 读取大文件时别让 pandas 一次性吞进内存

与写入类似,pandas 的read_excel很方便,但它本质上是把整个 sheet 读成 DataFrame,全部驻留在内存里。我处理过一张 80 万行的明细表,用 pandas 读入后内存占用直接超过 2GB,之后再做任何数据变换都极其吃力。

这种情况下我的首选是 openpyxl 的read_only模式,配合 Python 生成器逐行处理,把数据边读边转成目标结构。虽然代码没有 pandas 一行搞定那么简洁,但内存占用可以稳定控制在几百 MB 以内。

如果确实想让 pandas 吃下大文件,可以分块读:先记录总行数,再用skiprows和nrows分段读取,最后拼起来。这种方式牺牲了一些速度,但能把内存峰值压下来。

5.3 什么数据量该用哪种组合

这里有一个我自己的经验分界线,不一定是绝对标准,但可以帮新手上路:

  • 1 万行以内:openpyxl 普通模式完全够用,代码简单好维护。
  • 1 万到 10 万行:openpyxl 的 write_only 模式,或 pandas + openpyxl。
  • 10 万行以上:优先 xlsxwriter,如果需要 DataFrame,就用 pandas + xlsxwriter 引擎。
  • 读取超大 Excel:绝对优先 openpyxl 的 read_only 模式。

如果你的数据量到了百万行级别,我甚至建议换个思路:先让程序把 Excel 转成 csv 或数据库,再处理。Excel 本身不适合作为百万行数据的介质,硬上一个库也解决不了根本瓶颈。

6. 格式保留与兼容性:那些文档里没写的坑

写 Excel 跟写普通数据文件最大的不同,就是格式。这里有不少是文档不会告诉你的,但实际项目里特别容易翻车。

6.1 合并单元格读取时只有左上角有值

读取带合并单元格的 sheet 时,openpyxl 只会给合并区域的左上角单元格赋实际值,其余单元格读出来是None。如果你直接按行遍历,会漏掉大量信息。

我的处理方式是先检查ws.merged_cells.ranges,拿到所有合并区域,然后手动把左上角的值填充到整个区域:

from openpyxl import load_workbook wb = load_workbook('merged.xlsx') ws = wb.active # 收集所有合并区域 merge_map = {} for m_range in list(ws.merged_cells.ranges): top_value = ws.cell(m_range.min_row, m_range.min_col).value for row in range(m_range.min_row, m_range.max_row + 1): for col in range(m_range.min_col, m_range.max_col + 1): merge_map[(row, col)] = top_value

这种做法在报表解析、对账系统里非常实用。我曾在某个数据迁移项目里因为漏了这一步,结果合并单元格下面的一大片区域全变成了空数据,最终对账差了十几万。

6.2 条件格式和数据验证在不同环节的保留情况

openpyxl 可以读取部分条件格式规则,但遇到复杂规则(图标集、三色刻度、数据条)时,不同版本表现不一致。xlsxwriter 写条件格式很轻松,但它只写不读,所以不存在“读取保留”的问题。真正容易踩坑的是模板文件另存。

我给客户做过一个合同模板,里面有一列是“合同状态”,设置了数据验证下拉框和条件格式。第二次打开时 openpyxl 往里填了数据并另存,结果发现下拉框还能用,但某些单元格的条件格式变乱了。原因是一些下拉框关联的是“同一工作表内的名称区域”,另存时引用区域发生了偏移。

所以我的经验是:模板文件在交付前,一定先用业务里真实会用到的全流程跑一遍,检查样式、下拉、格式是否都符合预期。别等最后生成完才发现问题。

6.3 数字格式和日期序列号的常见混淆

Excel 内部把日期存成序列号,比如 2025-06-01 在单元格里的本质是一个数字。如果你用 openpyxl 写入 Python 的 datetime 对象,它会自动转换成日期类型,但如果你从别处拿到的是一个普通数字,直接写入就会变成45609这种“裸数字”。

正确的做法是写入后单独设置数字格式:

from openpyxl.styles import numbers cell = ws['B2'] cell.value = 45609 cell.number_format = numbers.FORMAT_DATE_YYYYMMDD2 # 'yyyy-mm-dd'

xlsxwriter 的做法不太一样,它有专门的write_datetime方法,需要传入 datetime 对象和格式对象:

import datetime as dt date_fmt = workbook.add_format({'num_format': 'yyyy-mm-dd'}) worksheet.write_datetime('A1', dt.datetime(2025, 6, 1), date_fmt)

很多报表生成脚本跑完发现日期列全是数字,就是因为忘了设置number_format。这个坑特别小,但几乎每个新手都会踩一次。

7. 最终选型建议与我的使用习惯

7.1 一张决策表覆盖八成需求

如果你看到这里还是有点乱,可以直接用下面这张决策表做选型,基本覆盖常规项目的需求:

需求场景推荐方案关键理由
读 .xlsx,文件不大pandas.read_excel简单直接
读 .xlsx,文件很大openpyxl read_only内存可控
读 .xls 老文件xlrd(锁 1.2.0)唯一稳定支持
把 DataFrame 写进 Excelpandas + xlsxwriter速度与统计能力平衡
修改模板文件并保留样式openpyxl唯一可读写改样式的稳定组合
从零生成带图表条件的报表xlsxwriter性能与样式能力最优
格式混杂的小项目快速处理pyexcel代码统一、简单

这个表是我在多个项目里反复验证过的组合。它不保证每个极限场景最优,但一定不会让你在三更半夜排查莫名报错。

7.2 我沉淀下来的一些使用习惯

最后分享几点我长期实践后觉得特别重要的使用习惯。

第一,混合使用不犯法,但要清楚边界。我的一个常见组合是:pandas 负责数据清洗和统计,xlsxwriter 负责把结果生成漂亮报表,openpyxl 负责在最后阶段打开文件做补充修改。这三个库各干各的事,互不干扰,问题反而最少。

第二,新项目一律统一用 .xlsx,能不用 .xls 就不用 .xls。.xls 老格式带来的兼容性问题远多于收益。如果客户坚持给 .xls,我一般会先让脚本把文件转成 .xlsx,再走主流程。

第三,写文件之后一定要做“二次验证”。很多库生成的 .xlsx 表面看起来没问题,打开时却提示损坏或样式丢失。我现在养成了一个习惯:每个导出任务跑完后,用 openpyxl 再打开一次生成的文件,检查 sheet 数量、行数、关键单元格的值和样式是否符合预期。这一步成本很低,却能提前拦下绝大多数交付事故。

如果你正面临选型问题,希望这篇对比能帮你一次性理清思路。代码可以直接拿去用,踩坑的经验也已经标出来了,剩下的就是拿真实数据跑一遍,看看哪种组合最顺手。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询