Python批量修改200+Excel工作表:openpyxl高效实现指南
2026/9/15 12:19:23 网站建设 项目流程

简介:Python批量更改Excel文件中200多个工作表内容的实例资源包,面向需要处理多工作表Excel数据、希望用脚本提升办公效率的Python初中级学习者。资源以Python与pandas/openpyxl为核心,完整演示了加载多工作表Excel、遍历所有工作表、按条件批量修改单元格数值、使用inplace优化与分块读写应对大型数据集等方法。压缩包整体约3.14MB,包含可直接运行的演示脚本与配套示例数据,结构清晰便于对照练习。目前已有123人学习下载,适合正在做Excel自动化报表、需要批量清洗或更新工作表字段的数据处理人员。通过该资源可掌握多工作表批量操作的核心套路,并能将同一思路迁移到合并单元格、条件筛选、向Word填充数据等常见办公自动化场景,显著减少手工重复操作。

1. 200 多个工作表批量改内容,Python 的打开方式

一张 Excel 工作簿里躺着 200 多个工作表,结构相同,但某个单元格的值要统一改、某些区域的公式要整体换、部分 sheet 名要重排。手工模式意味着每个 sheet 都要点进去、定位、粘贴、核对,200 个下来,时间成本足以让人怀疑人生。

用 Python 批量处理这条路并不神秘:openpyxl 把整个工作簿解析成内存对象,程序化地遍历工作表、定位单元格、改写内容、一次性保存,全程不依赖 Excel 是否安装,跑完还能自动输出改动清单。

下面要讲的就是这个场景的完整落地路径:库选型、遍历逻辑、格式保护、性能优化、打包交付。工作簿里有 200 个还是 2000 个 sheet,核心思路一致。

2. 选对工具:openpyxl、xlwings、pandas 在批量改表场景的取舍

2.1 三种库各自管到哪一层

Python 处理 Excel 的库不少,但"批量修改已有工作表内容"这个需求下,值得对比的只有三个:openpyxl、xlwings、pandas。

pandas 擅长把表当数据算,read_excel 读出来就是 DataFrame,批量改值很快,但写回时对原有格式的破坏几乎是毁灭性的——列宽、合并单元格、背景色、数据验证全部丢失。如果只是算完导出新表,pandas 没问题;要在原文件基础上改 200 个 sheet 还保住格式,pandas 不是第一选择。

xlwings 走 COM 自动化路线,底层驱动真实 Excel 进程,能力上限等同于 Excel 本身:VBA 能做的它基本都能做,包括调用内置函数重算、操作图表、触发事件。代价是必须装 Excel(Windows 或 macOS),Linux 服务器上跑不了,而且 200 个 sheet 逐单元格操作时,COM 调用的开销会让速度慢一个量级。

openpyxl 是纯 Python 实现对 xlsx 的读写,不依赖 Excel 进程。它把工作簿解析成内存对象,样式、公式、合并单元格都能保留。缺点是没有 Excel 引擎,不会帮你重算公式,对 .xls 老格式也不支持。批量改 200+ 工作表这个场景,openpyxl 是多数情况下最稳的默认选择。

2.2 先用只读模式摸底,再决定怎么改

200 多个 sheet 的 xlsx,如果每个 sheet 有几百行几十列,整个工作簿可能吃掉几百 MB 内存。load_workbook 的 read_only 模式按行流式读取,内存占用低,但只能读不能改。真正要修改并保存,必须用普通模式加载。

常见做法是分两步:先用 read_only 快速打印 sheet 清单和每个 sheet 的维度,确认结构差异,再用普通模式加载执行修改。

from openpyxl import load_workbook wb = load_workbook('sales_report.xlsx', read_only=True) for name in wb.sheetnames: ws = wb[name] print(f'sheet={name}, 行数={ws.max_row}, 列数={ws.max_column}') wb.close()

这段代码用 read_only=True 加载,wb.sheetnames 返回所有工作表名称,ws.max_row 和 ws.max_column 给出每个 sheet 的已用区域尺寸。注意 read_only 模式下如果需要遍历单元格值,必须用 ws.iter_rows() 而不是按坐标取值,因为流式模式下单元格对象是边读边生成的,直接 ws['A1'] 这类访问可能取不到。

2.3 环境准备:装 openpyxl 的最小命令与版本要求

python -m pip install openpyxl

如果刚配好 vscode 的 python 环境,或者本机有多个 Python 版本,用 python -m pip 而不是裸 pip,能保证装进当前解释器对应的环境里。装完验证版本:

python -c "import openpyxl; print(openpyxl.__version__)"
检查项命令预期结果
Python 版本python --version3.8 及以上
openpyxl 版本python -c "import openpyxl; print(openpyxl.version)"3.1.x 或更高
写入测试python -c "from openpyxl import Workbook; Workbook().save('/tmp/t.xlsx')"无报错,文件生成

openpyxl 3.1 之后的版本对样式保留、条件格式的支持都比较完善。如果项目里锁的还是 2.6 或 3.0,建议升到 3.1 以上再跑批量修改,老版本在处理合并单元格区间和主题色时偶尔会踩坑。

3. 遍历 200+ 工作表并精准定位目标内容的实现方案

3.1 摸底:打印每个 sheet 的前几行和公式原文

改动之前先搞清楚"改哪里"。200 多个 sheet 一般有两种情况:要么结构完全一致(每个 sheet 是某个分公司的月报,B2 放公司名,D5 放合计值),要么结构相近但列位置有偏移。先跑一段探查代码,打印每个 sheet 的前五行,肉眼比对结构差异。

from openpyxl import load_workbook wb = load_workbook('consolidated.xlsx', read_only=True, data_only=False) for name in wb.sheetnames: ws = wb[name] print(f'----- {name} (行 {ws.max_row}, 列 {ws.max_column}) -----') for row in ws.iter_rows(min_row=1, max_row=5, values_only=True): print(row) wb.close()

data_only=False 是刻意设置的:如果写成 True,公式单元格读到的是缓存的计算结果;False 才能拿到公式原文。做批量修改时,公式原文比结果值更重要,因为你要判断这个格子能不能动、动了之后引用链会不会断。

3.2 按坐标批量改写单元格值的核心代码

假设需求:把每个 sheet 的 C3 改成当前 sheet 名称(把固定标签换成具体分表名),同时把 F10 的数值统一乘 0.9。代码如下:

from openpyxl import load_workbook SRC = 'consolidated.xlsx' RATIO = 0.9 wb = load_workbook(SRC) # 默认 data_only=False,保留公式原文 for ws in wb.worksheets: # 改标签:C3 写入当前 sheet 名称 ws['C3'] = ws.title # 改数值:F10 是数字才乘系数,是公式或文本则跳过 cell_f10 = ws['F10'] if isinstance(cell_f10.value, (int, float)): cell_f10.value = round(cell_f10.value * RATIO, 2) wb.save('consolidated_updated.xlsx')

逻辑说明:wb.worksheets 返回所有 Worksheet 对象,循环内按坐标赋值即可。isinstance 判断是必须的——如果 F10 里是公式字符串 '=SUM(F1:F9)',直接乘 0.9 会抛出 TypeError。round(..., 2) 把结果限制到两位小数,避免浮点误差在 200 个 sheet 里扩散成汇总对不齐。

如果目标是按关键词替换,而不是固定坐标,用遍历加字符串判断:

from openpyxl import load_workbook wb = load_workbook('consolidated.xlsx') OLD, NEW = '2023年', '2024年' for ws in wb.worksheets: for row in ws.iter_rows(): for cell in row: if isinstance(cell.value, str) and OLD in cell.value: cell.value = cell.value.replace(OLD, NEW) wb.save('consolidated_updated.xlsx')

iter_rows() 不传范围会遍历整个已用区域,200 个 sheet 全量扫描,逻辑简单但耗时。优化手段是先用 max_row 和 max_column 把范围限制到真正需要检查的行列区间,比如只扫 A 到 H 列,能省掉接近一半的无效遍历。

3.3 用映射表驱动:不同 sheet 改不同内容

更复杂的场景:sheet 名称不同,改动规则也不同。名称含"华东"的 sheet 把毛利率阈值改成 0.25,含"华南"的改成 0.2,其他不动。这种按规则分流的需求,用字典映射最清晰:

from openpyxl import load_workbook wb = load_workbook('regional.xlsx') THRESHOLD = { '华东': 0.25, '华南': 0.20, '华北': 0.22, } for ws in wb.worksheets: key = ws.title for region, val in THRESHOLD.items(): if region in key: ws['E7'] = val # 给已处理的 sheet 标签着色,方便完成后抽查 ws.sheet_properties.tabColor = 'FFC000' break wb.save('regional_updated.xlsx')

sheet_properties.tabColor 给工作表标签着色,属于可选的视觉标记,抽查时一眼能分辨"已处理"和"被跳过"。break 保证一个 sheet 只命中第一条规则,避免"华东"和"华东二部"这类名称同时匹配两条映射造成重复赋值。

3.4 把坐标和规则集中到 config,避免改错位置

200+ sheet 的批量改动,最怕改到一半发现坐标写错。我一般会把所有可调参数收敛到文件顶部或单独 config.py:

参数名类型含义示例值
SRC / DSTstr源文件与输出文件路径'input.xlsx' / 'output.xlsx'
TARGET_CELLstr目标单元格坐标'C3'
MAPPINGdictsheet 名关键词 → 新值{'华东': 0.25}
SKIP_CELLSlist需要跳过或特殊处理的公式单元格['F10']
SCAN_MAX_COLSint遍历列数上限20

配置和逻辑分离之后,换一批文件只改配置不动代码。顺手给输出路径加上时间戳,防止覆盖上次结果:

from datetime import datetime DST = f"output_{datetime.now():%Y%m%d_%H%M%S}.xlsx" wb.save(DST)

注意:所有修改做完只 save 一次,绝不要在循环里反复 save。每 save 一次就要全量序列化一遍整个工作簿,200 个 sheet 的文件一次保存 5 秒,循环里保存 200 次就是 1000 秒。

4. 大批量改写的性能瓶颈与格式保护

4.1 read_only 不能改,普通模式内存爆了怎么办

read_only 模式是流式读取,迭代器消费完一行就释放一行,内存占用低,但它的定位就是"读取优化",不是轻量编辑。在这个模式下改单元格值再保存,openpyxl 要么直接报错,要么写出的文件丢失大量内容,不能用来做修改。

加载模式可读可写内存占用适用场景
默认(普通)修改已有文件并保存
read_only探查结构、提取数据
write_only仅追加行仅新建从零生成大文件

那内存不够怎么办?常见做法是先看工作簿体积:200 个 sheet 的文件通常 10-50 MB,普通模式加载后占用 300-800 MB 内存,本机 8 GB 内存基本能扛住。如果文件超过 200 MB,普通模式可能直接 OOM,这时候要么拆文件处理(read_only 读出内容,按 sheet 粒度分批重建),要么考虑换服务端方案。业务代码里一般不推荐直接解压 xlsx(它本身是 zip 结构)去做 XML 级替换,速度虽快,但样式、行列属性的 XML 结构一旦改错,整个文件就打不开了。

4.2 改值不动样式:避开字体覆盖的坑

用 openpyxl 加载再保存,默认会保留字体、边框、填充、列宽等样式信息,前提是不要主动动 cell.font / cell.fill 这类属性。下面这种写法要避免:

# 错误示范:整段重建字体属性,原有颜色、加粗全部丢失 from openpyxl.styles import Font cell = ws['C3'] cell.font = Font(name='Arial', size=11)

如果确实要改字体,先复制原对象再改子属性:

from copy import copy from openpyxl.styles import Font cell = ws['C3'] old = cell.font cell.font = Font(name='Arial', size=old.size, bold=old.bold, color=old.color)

但多数批量改内容的需求根本不需要碰样式,默认做法就是只改 value。另一个容易忽略的点:wb.save 保存后,原文件里部分图表、图片可能丢失,openpyxl 对这类对象的覆盖一直不是 100%。

提示:先复制一份原文件再跑脚本,openpyxl 保存时不会对源文件做任何保护,一次误操作就是全量损失。

4.3 合并单元格和公式单元格的特殊处理

改内容时,合并单元格是最常见的坑。区域 A1:C1 合并后,只有左上角有值,右下角访问到的是 None。直接给非左上角单元格赋值,写入可能成功,但 Excel 打开时会提示文件损坏需要修复。

处理方式是先收集合并区域,跳过非左上角单元格:

from openpyxl.utils.cell import range_boundaries for ws in wb.worksheets: protected = set() for mr in ws.merged_cells.ranges: min_col, min_row, max_col, max_row = range_boundaries(str(mr)) for r in range(min_row, max_row + 1): for c in range(min_col, max_col + 1): if (r, c) != (min_row, min_col): protected.add((r, c)) # 遍历改写时若 (row, col) 在 protected 里则跳过

公式单元格的原则是"能不动就不动"。如果只改公式引用的源单元格,重算后公式会取到新值;如果改了公式本身,openpyxl 不会验证语法,错误公式不会在保存时报错,而是打开文件时 Excel 才提示。批量写公式前,至少挑两三个 sheet 用 Excel 或 LibreOffice 验证一遍重算结果。

4.4 200 个 sheet 的写入提速经验

实测下来,200 个 sheet 的中等规模文件,openpyxl 保存时间通常在 5-30 秒量级,瓶颈在 XML 序列化和压缩。几个提速手段:

手段效果代价
限定遍历行列范围遍历耗时明显下降逻辑稍复杂
全程只 save 一次避免重复序列化无副作用
加载时用 data_only=False少读一层缓存值校验阶段需另行处理
关闭无用属性访问减少对象构造开销影响可忽略

这些手段里,"只 save 一次"收益最大,也最容易做到。其他的属于锦上添花,文件不大时感受不明显。另外注意:工作表格式化相关的操作(比如批量调整列宽、设置数字格式)如果超过几百个单元格,逐格设置样式会非常慢,常见做法是整列设置 ColumnDimension,而不是逐格改。

5. 打包成可交付的 zip 项目并自动验证改动结果

5.1 最小可交付的项目目录与 zipfile 打包

脚本要交付给同事或客户,不能只丢一个 .py 文件。常见做法是把脚本、配置、说明整理成固定目录,再压缩成 zip:

excel_batch_updater/ ├── config.py # 所有可调参数 ├── updater.py # 主脚本 ├── verify.py # 验证脚本 ├── requirements.txt # openpyxl>=3.1 └── README.md # 使用说明

用 Python 自带的 zipfile 模块打包,不需要额外装工具:

import zipfile from pathlib import Path src_dir = Path('excel_batch_updater') with zipfile.ZipFile('excel_batch_updater.zip', 'w', zipfile.ZIP_DEFLATED) as zf: for f in src_dir.rglob('*'): if f.is_file(): zf.write(f, f.relative_to(src_dir.parent)) print('打包完成:excel_batch_updater.zip')

ZIP_DEFLATED 表示 deflate 压缩算法,脚本体积不大时效果不明显,但目录里如果带了样例 xlsx,压缩率通常能到 90% 以上。rglob('*') 递归收集所有文件,relative_to 保证 zip 内的路径不带上层目录名,解压后直接是项目根目录。

5.2 验证脚本:全表扫描核对替换是否到位

改完不能只靠眼睛抽查 200 个 sheet,写一个 verify 脚本自动核对:

from openpyxl import load_workbook SRC = 'consolidated_updated.xlsx' OLD = '2023年' wb = load_workbook(SRC, read_only=True, data_only=True) errors = [] for ws in wb.worksheets: for row in ws.iter_rows(): for cell in row: if isinstance(cell.value, str) and OLD in cell.value: errors.append(f'{ws.title}!{cell.coordinate}: {cell.value}') if errors: print(f'校验失败,共 {len(errors)} 处未替换,前 20 条:') for e in errors[:20]: print(' ', e) else: print(f'校验通过:{len(wb.sheetnames)} 个 sheet 全部替换完成') wb.close()

data_only=True 是验证阶段的关键——关心的是最终展示值,而不是公式原文。如果某个格子是公式且缓存值还是旧文本,说明公式重算没发生或源数据没改对。把错误数量输出,比肉眼翻 200 个 sheet 可靠得多。

5.3 验证维度与输出文件命名技巧

验证项方法通过标准
内容替换率全表扫描旧关键词旧关键词出现次数为 0
数值变更抽查 5-10 个 sheet 的汇总值与预期计算一致
格式完整性用 Excel/LibreOffice 打开并随机滚动无样式异常、无修复提示
文件可打开用 openpyxl 重新 load_workbook不抛异常

最后一个技巧:输出文件名带上版本后缀(如 _v2.xlsx),不要覆盖源文件。批量改表这类操作一旦覆盖原文件,找回原始数据只能靠版本历史或备份,而大多数项目没有给 Excel 配版本管理。保留源文件、输出到新文件,是成本最低的安全兜底。交付时把 zip 里的 README 写清楚运行参数,同事拿到后只需要执行 python updater.py 和 python verify.py 两条命令。

本文还有配套的精品资源,点击获取

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

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

立即咨询