1. 先别急着写代码搞清楚 openpyxl 到底解决什么问题说实话我在日常处理数据的时候Excel 几乎是无处不在的。别人发来的业绩报表是 xlsx领导要的周报模板是 xlsx就连我自己的记账表、项目进度表也都是 xlsx。数据量小的时候鼠标点一点还好但一旦到了几百行、上千行或者需要一批一批地生成报告光靠手工复制粘贴简直就是在折磨自己。所以我一直觉得任何一个和数据打交道的人都应该会一个能操作 Excel 的 Python 库而 openpyxl 就是其中最直接、最顺手的一个。openpyxl 是一个纯 Python 编写的库专门用来读写 Excel 2007 及以后版本的 xlsx/xlsm 文件。它能做的事情很具体读取单元格的值、写入新的数据、设置单元格样式、合并单元格、插入图表、操作公式、筛选排序甚至还能处理条件格式。它不像 pandas 那样上来就给你一个 DataFrame而是更贴近 Excel 这个软件本身的逻辑你有一个工作簿工作簿里有多个工作表每个工作表里有行列坐标对应的单元格。这种模型让很多熟悉 Excel 操作的人上手非常快因为你脑子里想的“我在表格里填一个数”“我把这个格子标红加粗”几乎都能直接翻译成 openpyxl 的代码。什么人适合用这个库我觉得主要有三类第一类是经常做报表的职场人你可能天天要处理销售明细、库存清单与其每次都手动复制公式不如写一个小脚本一劳永逸第二类是测试和数据分析的初学者你刚好需要批量准备测试数据或者做简单的数据清洗又不想被 pandas 的很多概念绕晕第三类是写自动化脚本的开发者比如定时从某个系统导出数据后自动整理成固定格式的 Excel 文件。这篇文章我会从安装开始把读写操作中最高频、最实用的部分完整过一遍包括我踩过的坑和折腾出来的小技巧尽量让你看完就能直接上手。2. 为什么偏偏选 openpyxl而不是 pandas、xlrd 或者 xlsxwriter很多新手拿到“用 Python 操作 Excel”这个需求时最先接触到的可能是 pandas因为网上到处都在说 pandas 处理 Excel 很方便。pandas 确实方便——pd.read_excel()一行读进来df.to_excel()一行写出去但问题在于 pandas 的重点是“数据分析”它把 Excel 当成一个数据流入流出的接口而不是一个精细操作用的画布。你要做数据透视、分组聚合、统计分析pandas 就是最优解但如果你要打开一个已经有很多格式的模板文件往里面指定位置填几个数再调整某个单元格的背景色和边框pandas 就很别扭了它不擅长也不推荐做这种事情。大家常听到的另外几个库也各有短板。xlrd 和 xlwt 是老牌库操作的是 Excel 97-2003 的 xls 格式放到今天来看xls 格式本身已经不够主流xlrd 的新版本甚至已经不再支持读取 xlsx 以外的格式了维护状态也不乐观。xlsxwriter 主要用来“写入”新文件功能上很强大支持图表、图片、条件格式都有但它不能读取已有的 xlsx 文件也没办法修改一个现存的工作簿。所以如果你业务里既要读又要写还希望保留原有的格式不被破坏xlsxwriter 就不够用了。openpyxl 目前是这几个选项里综合能力最均衡的它既能读也能写既能处理全新的文件也能处理已有的模板它对 xlsx 格式的支持很完整单元格样式、合并区域、冻结窗格、数据验证这些都能操作而且它不依赖 pandas随便配个 Python 环境就能跑轻量干净。特别是在“批量修改报表”“按模板填充数据”这种场景里openpyxl 几乎是不可替代的。我这里给一张表把几个常见库的适用情况说一下方便你按需选型。库名能读能写修改已有文件支持格式适合场景openpyxl是是是xlsx / xlsm精细操作、模板填充、样式调整pandas是是有限xlsx / xls 等数据分析、批量转表、聚合统计xlrd是否否xls 为主只读老格式注意新版限制xlwt否是否xls写老格式xlsxwriter否是否xlsx纯新建和写图表、写大文件所以结论很简单如果你要的是“把 Excel 当成一个可以被程序精细控制的对象”用 openpyxl 就对了。3. 环境准备和基础概念10 分钟跑通第一个读写 Demo3.1 安装 openpyxl一条命令的事openpyxl 的安装没有那么多花活直接用 pip 安装就行。我个人建议你不管做什么项目都先用虚拟环境隔离一下依赖避免全局环境被搞成一团乱麻。具体操作如下python -m venv excel_env source excel_env/bin/activate # Windows 下执行 excel_env\Scripts\activate pip install openpyxl装完以后在 Python 里执行import openpyxl不报错就说明环境没问题了。如果你是在公司内网离线环境里也可以先从官网下载 whl 文件再用pip install xxx.whl安装。这一步实在没什么难度我唯一要提醒的是不要用sudo pip或者直接在系统全局环境里装倒不是说会出多大的事只是后续很容易出现依赖版本冲突到时候排查起来很头疼。3.2 一个能存活的读写 Demo我先给一个最精简但是流程完整的示例你把这个脚本跑通就相当于已经知道了 openpyxl 80% 的日常工作模式。import openpyxl # 1. 创建一个新工作簿 wb openpyxl.Workbook() ws wb.active ws.title 成绩单 # 2. 写入数据 ws[A1] 姓名 ws[B1] 语文 ws[C1] 数学 students [ (张三, 88, 95), (李四, 79, 92), (王五, 93, 84), ] for row in students: ws.append(row) # 3. 保存文件 wb.save(成绩单.xlsx)这段代码做完的事是新建一个工作簿把默认工作表改名为“成绩单”写入标题行再逐行追加学生数据最后把整个工作簿保存为成绩单.xlsx。注意ws.append(row)这个方法很常用它表示“从当前工作表的第一行还没有数据的空白位置开始往下一行一行地追加数据”。如果你第一次运行工作表是空的那它就从第 1 行开始写下一次再往同一个工作表追加就自动跳到第 6 行因为前面已经有 5 行数据了。再来看一个读取的完整示例import openpyxl wb openpyxl.load_workbook(成绩单.xlsx) ws wb[成绩单] # 读取单个单元格 print(ws[A1].value) print(ws.cell(row2, column2).value) # 读取全部数据 for row in ws.iter_rows(min_row1, max_rowws.max_row, max_colws.max_column, values_onlyTrue): print(row)这个例子里你第一次接触到了两个读取姿势一种是直接用坐标索引ws[A1]和 Excel 里单元格的名称是一致的符合直觉另一种是用ws.cell(row2, column2)适合在循环里用变量方式动态指定行列。max_row和max_column是两个很重要的属性表示当前工作表中有数据的最大行号和最大列号遍历全表的时候经常拿它们来做边界。3.3 关于 Workbook、Worksheet、Cell 这三个概念把我刚才的代码拆开看你会发现 openpyxl 的核心对象就是三层最外层是 Workbook对应一个 Excel 文件中间是 Worksheet对应文件里的一个表就是左下角那些 tab最里层是 Cell对应一个单元格。日常操作基本就是这三层之间来回折腾。Workbook 可以理解为整个文件的内存对象。你用openpyxl.Workbook()创建的是一个空白工作簿里面会默认带一个名为 Sheet 的工作表用openpyxl.load_workbook(xxx.xlsx)加载的是磁盘上已经存在的文件。操作完之后调用wb.save(xxx.xlsx)把内存里的内容写回磁盘。这里藏着一个容易踩的坑save()是覆盖写。如果你加载了一个原来叫report.xlsx的文件修改之后又直接save(report.xlsx)原文件就被覆盖了没有撤销的机会。所以我在处理重要文件的时候习惯先另存成一个新文件名确认没问题再替换。Worksheet 就是你正在编辑的那个工作簿的某一个 sheet。wb.active表示当前默认激活的那个工作表wb[成绩单]是按名字取wb.create_sheet(title新表)是新建。如果你需要遍历所有工作表wb.sheetnames能拿到一个包含所有表名的列表这一招在合并多个 sheet 数据的时候特别好用。Cell 是工作表里的最小单位通过ws[A1]或ws.cell(row1, column1)取到。一个单元格对象的核心属性就是.value读取是拿它的值赋值是设置它的值。一个让很多人不太适应的地方在于就算你写ws[A1] hello之后再去读也得通过.value才能拿到内容直接打印ws[A1]出来的是一坨对象的内存信息不是 hello。4. 核心读写操作从单元格到区域把常用操作全部吃透4.1 单元格的读取和写入两种写法都要熟练读写单元格听起来很简单但不同的写法背后对应着不同的编程场景。直接下标法ws[B2] 100是给人看的代码清晰直白ws.cell(row2, column2, value100)是给程序看的适合写在循环里因为它接受的是整数参数可以随变量变化。如果你要批量给某一列填数据不要一个一个地用ws[B1]、ws[B2]这种写法去赋值尽量用append或者先构建好数据列表再统一写入。举个反例我曾经见过一个把 3000 行数据通过for i in range(3000): ws.cell(rowi1, column2).value data[i]写入的脚本效率其实也不差但代码读起来像上世纪的老式 BASIC不好维护。后来我改成先把数据整理成列表一次性操作脚本清晰了也方便后续加样式逻辑。批量读取同样如此。你当然可以for row in range(1, ws.max_row 1): for col in range(1, ws.max_column 1): ...但这种双重循环稍微复杂一点就容易把自己写晕。我更喜欢用iter_rows()和iter_cols()这两个方法它们直接在行或者列的方向上做迭代代码更短也不容易差一。# 按行读取每个元素代表一行内的一组单元格 for row in ws.iter_rows(min_row1, max_row3, min_col1, max_col2): for cell in row: print(cell.coordinate, cell.value) # 按行读取但只取单元格的值 for row in ws.iter_rows(min_row1, max_row3, min_col1, max_col2, values_onlyTrue): print(row)values_onlyTrue这个参数很常用它让迭代出来的东西不再是 Cell 对象而是直接是值在处理数据时更轻便。不过要注意如果一行里有空单元格values_onlyTrue模式下读出来就是None得自己处理空值。4.2 批量写入的三种姿势什么时候用 append什么时候改单元格我实际工作中用得最多的写法是append()前面已经说过了它自动找空白行往下追加适合从零开始搭建一组数据。还有一种情况是你要往指定区域放数据比如模板里已经规定好了 B3:D3 是合计区域那你就用坐标直接赋值。第三种情况是读入了一个二维列表想整体填到一个区域里这时候可以用ws[A1:C3] list_of_lists这种区间赋值方式它会按区间形状把数据填进去。注意区间赋值时如果数据和区间形状不匹配openpyxl 会直接报ValueError所以先assert一下行数列数最稳妥。我个人的经验是写新报表优先append()填模板优先坐标赋值区间赋值适合那种程序临时生成的大块数据。下面放一个区间赋值的小示例data [ [1, 2, 3], [4, 5, 6], [7, 8, 9], ] ws[A1:C3] data这段代码会把 1~9 九个数字分别填到 A1、B1、C1、A2、B2、C2 这样顺序的单元格里矩阵结构和区域结构刚好对应理解起来没有任何障碍。4.3 读单元格的坑data_only 参数必须搞清楚load_workbook()有一个非常容易踩的坑就是data_only参数。默认情况下它是False此时你读取一个 Excel 里用公式算出来的单元格拿到的不是结果显示而是公式字符串本身。比如 Excel 里某个单元格显示 95公式是AVERAGE(B2:C2)你用默认方式读得到的是AVERAGE(B2:C2)而不是 95。这常常让第一次遇到的人一头雾水以为数据丢了。解决办法是load_workbook(xxx.xlsx, data_onlyTrue)。这种模式下openpyxl 会读取 Excel 在保存文件时缓存下来的公式计算结果。但这里面又有一个新的坑如果这个文件是用 openpyxl 创建后直接保存的Python 这一侧只保存了公式、并没有用 Excel 打开去算过那data_onlyTrue读出来的值就是None。因为这时候文件里根本没有可以被读取的公式结果缓存。读取方式公式单元格读回来的是什么适用场景data_onlyFalse公式字符串如A1*2查看或修改公式本身data_onlyTrue公式的结果如 88读取计算后的数据都没有缓存None用 openpyxl 写公式后未用真实 Excel 打开过所以我现在的习惯是如果 Excel 文件是别人用 WPS 或者 Office 保存过的我load_workbook(filename, data_onlyTrue)如果这个文件是我自己脚本临时生成并且里面需要公式那我就在写完公式之后调用一个本地 Excel 应用打开再保存一次或者干脆不依赖公式直接在 Python 里把计算好的值写进去。追求自动化的时候就放弃公式直接用结果省得两头摸黑。4.4 样式设置字体、对齐、边框、填充色很多人写 openpyxl 只填数据不加样式然后拿出去的文件丑得没法看。其实设置样式也不复杂核心就是几个类Font管字体PatternFill管背景填充Alignment管对齐和换行Border和Side管边框openpyxl.styles模块里全都包含了。我举一个实际场景生成月度报表时给表头加个灰底、加粗、居中再给所有数据单元格加上边框、自动换行。from openpyxl.styles import Font, PatternFill, Alignment, Border, Side header_font Font(boldTrue, size12) header_fill PatternFill(start_colorD9D9D9, end_colorD9D9D9, fill_typesolid) center_alignment Alignment(horizontalcenter, verticalcenter) thin_side Side(stylethin, color000000) border_all Border(leftthin_side, rightthin_side, topthin_side, bottomthin_side) for col in range(1, ws.max_column 1): cell ws.cell(row1, columncol) cell.font header_font cell.fill header_fill cell.alignment center_alignment cell.border border_all这里有几个细节需要注意。PatternFill里的start_color和end_color对于solid类型的填充来说只有start_color是有效颜色end_color写不写都行但很多人会在这里卡一下。Side(stylethin)的style还有medium、dashed等选项如果你只是想要默认样式的细黑边框thin就够了。边框必须四个方向都设置才完整只设left的效果就是只有左边有框线。还有一个小技巧给整列设置宽度可以ws.column_dimensions[A].width 15给整行设置高度可以ws.row_dimensions[1].height 20。如果你数据里有长文本记得配合Alignment(wrap_textTrue)开启自动换行不然表格里会直接撑出去。4.5 合并单元格、冻结窗格和自动筛选这三个功能在手工做 Excel 时很常用openpyxl 也支持得很好。合并单元格用的是ws.merge_cells(A1:D1)合并之后左上角的那个单元格会保留值其他被合并的单元格的值会变成None。取消合并用ws.unmerge_cells(A1:D1)。需要注意的是合并单元格读取时只有左上角有值你如果想按行遍历区域被合并掉的单元格读出来的值是None别觉得是程序 bug这是 Excel 数据模型本身的逻辑。冻结窗格是ws.freeze_panes A2这句的意思是从 A2 这个位置之前的部分全部固定住。解释一下它的逻辑你写freeze_panes B2就表示从第二行开始向下滚动时第一行始终可见从第二列开始向右滚动时第一列始终可见。如果你写freeze_panes A2那第一行就被固定住了表头滚不掉这是做报表时最常用的一个设置。自动筛选是ws.auto_filter.ref ws.dimensions。这里有点绕ws.dimensions是工作表的整个有效数据范围比如A1:F120。把它指给auto_filter.refExcel 打开后第一行就会带上下拉筛选箭头非常实用。注意ws.dimensions的获取时机要等你把数据都写完之后再设置否则范围不准确。4.6 读取和写入日期、公式、超链接Excel 里日期是出了名的容易搞混。openpyxl 在写入日期时可以直接赋一个 Python 的datetime对象它内部会存成数字值并设置日期格式但为了显示效果我一般会再显式设置一下单元格的number_format比如yyyy-mm-dd。如果你不设置用 Excel 打开时可能看到的是一串含义不明的数字那其实是日期的底层序列值不是乱码。公式的写入也很直接你只要把字符串赋值给单元格即可比如ws[C2] SUM(A2:B2)。前面已经提过用 openpyxl 写进去的公式在文件里是没有计算结果缓存的如果你后续用 pandas 或者其他工具读取这个文件可能拿不到公式的值这是由文件格式决定的不是谁能改的。超链接可以直接cell.hyperlink https://example.com然后还可以顺便设置cell.style Hyperlink让他有可点击的外观。不过说实话超链接这个功能我日常用得少如果你只是在做数据导出这一项可以先跳过。5. 进阶实操批量整理 Excel用脚本解决重复劳动5.1 遍历工作簿里所有工作表把数据汇总起来我举个例子某公司每个月的考勤明细表里分了 12 个 sheet名字分别叫 1 月、2 月、一路到 12 月每张表的结构完全一样第一行是表头后面是每个人的出勤天数、加班工时。领导要求把所有表合并成一张总表。手工复制 12 次也算能完成但如果在脚本里遍历一次以后每个月都不用再手动干这个活了。import openpyxl wb openpyxl.load_workbook(考勤明细.xlsx, data_onlyTrue) all_rows [] for name in wb.sheetnames: ws wb[name] for row in ws.iter_rows(min_row2, values_onlyTrue): all_rows.append((name,) row) new_wb openpyxl.Workbook() new_ws new_wb.active new_ws.append((月份, 姓名, 出勤天数, 加班工时)) for row in all_rows: new_ws.append(row) new_wb.save(考勤总表.xlsx)这里有几个地方值得注意我在读取时用了data_onlyTrue因为考勤明细里的数据如果是公式生成的没有这个参数读回来就是一堆公式字符串根本没法汇总。iter_rows(min_row2, values_onlyTrue)直接从第二行开始跳过标题行。合并时我在每一行前面插入了月份信息这样汇总表里能看出每条数据来自哪个月。5.2 怎么处理“模板文件太卡”的问题只读模式和只写模式openpyxl 默认加载一个 xlsx 文件会把所有单元格都读进内存当你遇到一个几十 MB、几万行的文件时加载过程会非常慢甚至内存占用直接爆掉。这时候有两个优化手段read_onlyTrue和write_onlyTrue。load_workbook(big.xlsx, read_onlyTrue)模式下openpyxl 不会把所有工作表完整读入内存而是采用按需读取的方式遍历数据时速度很快、内存占用小。代价是这种模式只支持读取不支持修改工作表结构你也不能用它来改样式。适合的场景是你只需要从一个大文件里把数据抽出来转存或者统计。反过来write_onlyTrue是openpyxl.Workbook(write_onlyTrue)它创建的工作簿不允许读取但写入大数据时内存占用小很多。如果我要生成一个 10 万行的报表我会优先考虑这个模式。wb openpyxl.Workbook(write_onlyTrue) ws wb.create_sheet(title大表) ws.append([序号, 数值]) for i in range(1, 100001): ws.append([i, i * 2]) wb.save(大表.xlsx)但注意write_only模式下不能直接改单元格也没有.cell()方法只能append一行行加。你要提前把样式设置成整行也可能受到限制所以它不是万能的只是大数据场景下的一种取舍。5.3 把样式模板复用起来不要让代码变成一堆重复赋值每次处理新的 Excel 都要写一遍表头加粗、设边框、调列宽吗不用。我做法是写一个函数接收一个 worksheet 对象然后统一处理样式。以后所有脚本都用这个函数改动一处所有报表的样式都会跟着变。def style_header_row(ws, row1): for cell in ws[row]: cell.font Font(boldTrue, colorFFFFFF) cell.fill PatternFill(start_color4472C4, end_color4472C4, fill_typesolid) cell.alignment Alignment(horizontalcenter, verticalcenter) cell.border Border( leftSide(stylethin), rightSide(stylethin), topSide(stylethin), bottomSide(stylethin), )这种封装的收益是长期的。你第一次写可能觉得多花了一点时间但一个月后你要让表头从白字蓝底改成黑字黄底只需要改这一处地方而不是在几十个脚本里搜来搜去。5.4 用 pandas 和 openpyxl 配合很多人以为用了 openpyxl 就不能再用 pandas这俩其实是互补关系。我的常用套路是用 pandas 做复杂的数据清洗和统计拿到最终结果后用 openpyxl 做格式化输出。或者反过来用 openpyxl 从模板里读取配置信息然后交给 pandas 去分析。import pandas as pd from openpyxl.workbook import Workbook df pd.DataFrame({姓名: [张三, 李四], 成绩: [88, 95]}) wb Workbook() ws wb.active ws.append(df.columns.tolist()) for row in df.itertuples(indexFalse): ws.append(list(row)) wb.save(pandas_style.xlsx)pd.read_excel()底层其实也支持openpyxl作为引擎。所以如果你项目中已经装了 openpyxl那 pandas 读 xlsx 时默认用的引擎就是它。两者无缝衔接没有谁取代谁的问题只有什么时候用谁最合适的问题。6. 常见报错与排查技巧把这些坑提前填平6.1 FileNotFoundError路径不对是最大元凶这个报错最常见的原因不是文件不存在而是你给的路径错了。Python 在工作时相对路径是以当前工作目录为基准的如果你脚本在 A 文件夹Excel 文件在 B 文件夹光写一个文件名当然找不到。我的建议是无论什么场合文件路径都用os.path.join()或者pathlib.Path去拼不要手写字符串。另外 I/O 操作时把路径打印一下很多时候问题一眼就能看出来。6.2 PermissionError文件正被 Excel 占用Windows 上这是第二个高频报错。你用代码读取或者保存文件时如果那个 xlsx 正好被 Excel、WPS 打开着系统会拒绝访问抛PermissionError。这不是 openpyxl 的问题是操作系统级别的文件锁。解决办法很简单先关掉 Excel 再跑脚本。我吃过很多次这个亏。以前有个脚本是每天早上批量生成报表结果每过几天就有人反馈说报错排查到最后发现是某个同事习惯性地把昨天的报表打开在那里看锁住了文件。后来我在代码里加了异常捕获遇到PermissionError就提示“请关闭已打开的 Excel 文件后重试”这才算彻底把人从排查里解放出来。6.3 NoneType 错误公式缓存惹的祸报错信息是AttributeError: NoneType object has no attribute value或者你也可能直接打印出None。原因我在前面已经说过data_onlyTrue读取一个没有计算结果缓存的公式单元格时值就是None。所以排查思路是先确认你的文件是谁生成的再用data_onlyFalse读一下看是不是公式字符串。如果确实需要公式计算结果最好的办法是让数据源先把值算好再落到 Excel 里。6.4 格式不支持xls 和 xlsx 不是一回事InvalidFileException这个报错一般是文件格式问题导致的。openpyxl 只支持 xlsx/xlsm不支持老版 xls。如果你拿到一个 xls 文件最好的办法是先用 Excel/WPS 另存为 xlsx 再让程序处理。如果这种操作太频繁你可以用工具批量转换或者干脆写个很小的函数遇到 xls 就调用另一个库读出来再转存。我个人的建议是业务上尽量统一 xlsx 格式大家沟通成本最低。6.5 数据明明在表格里循环读出来却少了行列这个现象通常出在max_row和max_column不准确时比如你的表格第一行和第二行之间有空行max_row会停留在最后一个有数据的行但如果空行出现在中间遍历时就把空行也读进来了。反过来如果表格最右边几列只有格式没有内容max_column可能比实际数据列数大。我常用的做法是用iter_rows()时不依赖max_column而是显式指定列范围或者用values_onlyTrue之后把None的列过滤掉。6.6 常见问题速查表报错信息可能原因解决思路FileNotFoundError路径不对或文件不存在检查工作目录用绝对路径PermissionError文件被 Excel/WPS 打开关闭相关程序后重试AttributeError: NoneType object...公式值无缓存改data_onlyFalse或先用真实 Excel 保存InvalidFileException文件不是 xlsx 格式转换为 xlsx 再处理ValueError: cells of the openpyxl...区间和数据结构不匹配检查二维列表的行列数zipfile.BadZipFile文件损坏或不是合法 xlsx用 Excel 重新打开另存7. 几个提高效率的实操心得我在实际使用 openpyxl 的这几年里最大的体会是这个库的 API 设计基本遵循了 Excel 的操作直觉所以学习成本很低但想要真正用得顺还是得靠多积累。这里分享几个我自己认为价值最高的心得。第一所有写入操作最后一定要记得wb.save()。这个听起来像废话但有相当多的人写完了忘了保存然后跑来问为什么数据没写进去。openpyxl 的所有变化都停留在内存对象里不调用save()全部白搭。我记得第一次写脚本时也干过这种事代码跑完没有任何报错打开文件一看什么都没变非常懵。第二调试时打印坐标和数据比什么都管用。比如你写了一个遍历脚本先打印一下ws.max_row和ws.max_column看看预期范围对不对。再打印一下ws[A1].value看读取对不对。很多问题不是逻辑错了是数据源就和你想的不一样。第三随手设置number_format。我见过太多导出后的 Excel 日期列显示成一串数字、百分比列显示成小数、金额列没有千分位。这个问题不是 openpyxl 的功能缺陷而是你没有告诉 Excel 这些单元格应该长什么样。一个number_format参数就能解决比如ws[B2].number_format 0.00%。第四如果要处理大量文件脚本跑之前先在少量文件上试一遍。比如你有 100 个 xlsx 要合并第一次可以只选 3 个跑确认输出结果格式正确、行数对得上再放全量跑。否则 100 个文件跑到第 90 个的时候突然报错排查起来很难受。8. 还能怎么继续扩展这个库的使用openpyxl 能做的事远不止读写。我在做一些自动化报表时还经常用到数据验证DataValidation来给单元格加下拉列表用条件格式FormatRule给超过阈值的数值标红甚至用图表模块往工作表里插入柱状图折线图。这些功能虽不算高频但在某些特定业务场景下能省掉非常多手工操作。如果你想系统地练习 openpyxl我给一个建议找一个自己手边真实存在的 Excel 表格先尝试只用代码读一遍再尝试生成一个格式好看的新表最后再做一个合并或拆分的操作。做完这三个练习openpyxl 的读写基础就算真正掌握了。之后你再看文档去探索样式、图表、数据验证等功能会发现一切都很自然因为你脑子里已经有了工作簿、工作表、单元格三者关系的清晰模型。最后再分享一个小经验写 openpyxl 脚本时尽量把读和写拆成两个函数或者两个阶段。比如一个脚本里先load_workbook把所有需要的数据读出来整理好再新建一个 workbook 写入。这样代码结构清楚也方便你中途打印数据排查问题。不要一边读一边写同一个文件那样不仅容易出错后面想复用这段代码也很麻烦。