ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于模板批量生成Excel文件的Python实战

基于模板批量生成Excel文件的Python实战 简介面向办公场景的批量生成表格文件小工具解决按统一模板逐人逐项创建独立文件的需求适用于考核表、工资单、成绩单等重复性制表工作。使用者无需编程基础只需维护好名单和模板工具便会按名单每一行单独生成一个表格文件文件名可在名单中自定义生成结果自动放进指定文件夹操作路径非常直观。压缩包共8个文件包含6个用于模板和名单的表格样例、1个可直接运行的主程序以及1个网页版使用说明整体大小约27.68MB。目前已有2562人学习下载适用于行政、人事、财务等需要高频批量发件的岗位。资源内附可运行程序、可编辑模板、名单示例和操作说明拿到后替换模板内容即可生成整套结果可大幅减少手工逐张制作表格的时间帮助办公人员更轻松地完成批量报表产出。 干过办公自动化的人应该都有过这种经历每个月月底要把几十上百个数据一模一样的格子填进无数张格式固定的Excel表格里。最开始我也试过录宏、手拖公式甚至复制粘贴到手腕发酸直到后来才明白这类事真正该做的不是“一次性操作”而是做一个“按模板批量生成Excel文件的小工具”。今天就拿我实际做过的一个项目说下完整思路包括模板怎么设计、数据源怎么整理、底层用什么库、代码怎么写以及过程中踩过的一些坑。如果你经常要处理报表分发、成绩单生成、合同信息导出这类需求或者打算自己写个运维小工具这篇文章应该能给你省下不少试错时间。1. 动手前先想清楚的三件事1.1 先确定是真“批量”还是伪批量很多朋友一上来就搜“C 批量生成excel文件”“Python批量操作Excel”然后套用某个库开始写代码。但我的建议是先冷静一下看看你的场景到底属于哪一种同一张表格按行填充不同数据最后导出成一个文件。这种场景最简单本质是数据写入。同一张模板表按某个字段拆分每个部门/每个人生成一个独立文件。这种才是真正意义上的批量生成也是本工具的核心场景。多个模板文件每个模板里都有固定的填空位用不同数据源去填。这种还需要考虑模板映射关系复杂度最高。这三种场景的代码结构完全不同如果一开始没分清楚写出来的工具大概率要返工。我那次需求是“每个员工一张工资条样式的Excel按部门打包”属于第二种下面所有讲解都会围绕这个场景展开。1.2 数据源决定工具的复杂度而不是模板很多人以为难点在Excel操作但实际操作下来数据源才是大头。如果数据源是一张规整的二维表比如工号姓名部门基本工资绩效工资001张三研发部80002000002李四市场部70001500那代码写起来非常轻松遍历每一行新建文件填充单元格保存完事。但如果数据源是来自多个Sheet、多个工作簿甚至是从某个后台接口拉回来的JSON那就要先做数据清洗和字段对齐。我建议所有数据操作都在内存里统一成“列表字典”的结构字段名和模板占位符严格一致后面填充时会省掉大量if/else。1.3 先想好输出方式再选技术方案输出方式有两种主流选择一是直接生成新的.xlsx文件二是复制现有模板文件再改写。前者适合模板本身简单、不需要保留复杂格式的场景后者适合模板有固定logo、固定边框、固定打印区域的场景也是我们这次采用的方式。这里有个常见的认知误区直接openpyxl.Workbook()新建一个工作簿然后往里填数据最后得到的文件往往没有原模板那种样式打印出来也不好看。而复制模板再填充等于把原文件当画布所有格式天然保留。简单说能用模板就不硬造格式这是批量生成小工具里最值得记住的一条经验。2. 模板语言思维占位符才是批量工具的灵魂2.1 用固定的占位符约定替代复杂逻辑你做批量工具时最难的不是“写数据”而是“怎么告诉程序数据填到哪个格子”。如果每个位置都用A1、B2这种单元格坐标写在代码里那模板稍微改一行代码就要跟着改。更优雅的做法是引入“模板字符串”思维——在Excel模板的单元格里预先写上占位符比如{name}、{dept}、{salary}程序读取这些占位符再替换成真实数据。这种思路和前端模板引擎、FastReport打印模板的设计逻辑一模一样本质都是“模板数据源分离”。我甚至见过用类似$P{name}、#para#这类语法做占位符的都不重要重要的是你要在模板里有一套统一且不易误匹配的约定。我个人的习惯是占位符统一用花括号包裹如{工号}、{姓名}。需要日期格式化的地方用{date:yyyy-MM-dd}这类带格式后缀的写法。数字金额类用{salary:#,##0.00}标记方便程序做格式化。2.2 模板设计的细节比你想的更重要模板不只是画个表格那么简单。以工资条为例最合理的布局是一个Sheet里预留一个“单条记录区域”比如第1行放表头第2行放占位符程序每次复制这两行的样式再往下写一条。不要在一个Sheet里把所有记录都铺好那样程序要定位的单元格太多容易错位也不要把所有数据塞到同一个Sheet里不同区域那样打印时分页会乱。具体到模板文件内部的命名我建议给每个Sheet一个稳定的名称程序里用代码锁定Sheet名称而不是索引。比如主模板Sheet叫Salary程序写死这个名称后续别人改模板只要不改Sheet名工具就不会崩。还有一个小细节模板文件里不要保留“测试数据”。有些人喜欢先用一行假数据排版做完后忘记删除结果程序填充完文件里既有测试数据又有真实数据这个问题排查起来特别费劲。所以模板定稿前一定要把示例数据清空只留下占位符和格式。3. 核心代码实现基于Pythonopenpyxl的批量生成器3.1 为什么选择Python和openpyxl热词里一搜“excel批量处理php”“c#后台处理前端传过来的excel”说明很多人习惯用什么语言就搜什么方案。但我的个人建议是批量生成Excel这种IO密集、模板操作居多的场景Python的openpyxl是最省心的选择。openpyxl 直接支持.xlsx格式读写都方便。能复制工作表、操作单元格样式、设置打印区域这些模板批处理需要的功能它都有。C 生成Excel文件不是不行但要么需要操作COM接口调起Office要么只能生成简单的XML表格格式遇到复杂模板样式修改会非常痛苦。C# 配合NPOI或ClosedXML也可以但代码量明显比Python大。当然如果你的环境完全不能装Python那C# ClosedXML会是更好的选择本质上思路一样都是“打开模板、查找占位符、替换值”。3.2 完整代码示例我这个工具实现思路是先读取Excel里的数据源一张明细表再用模板文件循环生成工资条。核心代码并不长核心就三步读数据、填模板、按部门保存。import shutil from pathlib import Path from openpyxl import load_workbook # 第一步读取数据源转成列表字典 def load_data(source_file): wb load_workbook(source_file, data_onlyTrue) ws wb[DataSource] # 数据源的Sheet名固定 headers [cell.value for cell in ws[1]] rows [] for row in ws.iter_rows(min_row2, values_onlyTrue): if all(v is None for v in row): continue rows.append(dict(zip(headers, row))) return rows # 第二步用模板行创建单条记录文件 def fill_salary_template(template_path, employee, output_path): # 每次从模板复制一份避免改动原模板 shutil.copy(template_path, output_path) wb load_workbook(output_path) ws wb[Salary] # 遍历模板中的占位符查找到后替换 for row in ws.iter_rows(): for cell in row: if not isinstance(cell.value, str): continue if cell.value.startswith({) and cell.value.endswith(}): key cell.value.strip({}) if key in employee: cell.value employee[key] else: # 找不到对应字段保留占位符并打印警告 print(f警告模板中的 {cell.value} 在数据源中不存在已保留) # 保存前设置打印区域和页边距保证输出看起来规整 ws.print_area ws.dimensions ws.page_setup.orientation portrait wb.save(output_path) # 第三步按部门循环批量生成 def batch_generate(source_file, template_path, output_dir): data load_data(source_file) output_dir Path(output_dir) output_dir.mkdir(parentsTrue, exist_okTrue) for emp in data: dept emp[部门] dept_dir output_dir / dept dept_dir.mkdir(exist_okTrue) filename f{emp[工号]}_{emp[姓名]}.xlsx fill_salary_template(template_path, emp, dept_dir / filename) print(f生成完成共 {len(data)} 个文件) if __name__ __main__: batch_generate( source_filedata.xlsx, template_pathtemplate.xlsx, output_diroutput )这里有几个地方我想单独说明。第一用shutil.copy复制模板而不是直接用load_workbook(template_path)修改后另存是为了防止程序中途崩溃导致原始模板被污染。这个坑我踩过以前直接操作原模板结果一次save失败后原文件里的公式和图表全部被打乱。复制一份再操作代价极低安全性高很多。第二遍历所有单元格查找占位符看起来效率不高但实际测试下来一个工资条模板最多几十个待填充单元格数据量撑死几千条记录整个程序跑完也就几秒钟。如果你的模板特别大、占位符特别多可以先用正则找出所有含花括号的单元格再逐个处理没必要优化到毫秒级。第三对ws.print_area和ws.page_setup的设置很多人会忽略。如果不设置打印区域用户打开生成的Excel按CtrlP一看发现表格被拆得乱七八糟。这一行代码解决的是“打印页数是否合理”的问题属于典型的“程序写完了但用户体验不好”的修补点。3.3 运行效果与进一步封装上述代码跑完后output目录下会生成按部门分好的子目录每个子目录里是按工号姓名命名的独立Excel文件。每个文件打开后格式和模板完全一致只有占位符被替换成真实数据。如果要把这个工具做成运维或给非技术同事使用我通常会加一个简单的Command Line参数解析让用户不修改代码就能指定数据源和输出目录python batch_excel.py --source data.xlsx --template template.xlsx --out output再进一步可以做成一个Windows下双击运行的exe把Python脚本用PyInstaller打包。打包的时候注意openpyxl本身是纯Python库打包后体积不大运行也稳定。只要目标机器装了Office能打开xlsx就不需要额外装任何环境。4. 常见问题与排查技巧实录4.1 模板单元格被改成了字符串而非数字这种情况是最常见的。比如数据源里“基本工资”是数字8000openpyxl写入后如果单元格原先是文本格式100%会变成文本型数字左上角会出现绿色小三角。后续如果别人要对这个单元格求和会直接计算错误。解决办法是数据源读取时提前把数字列转成float或int并且模板里给这些单元格设置好数字格式#,##0.00。实在不行就手动判断一下单元格类型from openpyxl.styles import numbers if key 基本工资: cell.value float(employee[key]) cell.number_format #,##0.004.2 生成的Excel打开后提示“文件已损坏”这个问题的元凶一般是load_workbook默认开启了数据只读模式或者不小心动了图表、图片。用load_workbook(output_path)时不加data_onlyTrue可以避免把公式缓存弄丢但如果你模板里有数据验证、图片、切片器等复杂对象openpyxl对它们的支持不是很完整保存时可能破坏文件结构。我的方案是能避免就避免模板里尽量少放「数据验证下拉框」这类高级功能如果非要保留建议改用C#的ClosedXML它对Excel高级特性的兼容性好很多。4.3 数据源里的文本有换行或前后空格工资条里出现“张三 ”这种带尾空格的名字或者地址字段里带换行符展示时很难发现但打印出来就怪怪的。我建议在读取数据源后统一做一次清洗def clean_value(value): if isinstance(value, str): return value.strip().replace(\r\n, \n) return value这个清洗函数虽然简单但在实际项目中能避免70%的表格对齐问题。4.4 大批量生成时内存越占越大几千个文件循环生成如果每次都不释放工作簿对象内存会持续增长。所以在fill_salary_template末尾除了wb.save()我还习惯加一句wb.close()虽然openpyxl在工作簿对象被覆盖时也会释放但显式调用close()能让内存回收更及时。生成几千个文件时这个习惯能明显降低内存峰值。4.5 文件名中的非法字符如果按员工姓名做文件名遇到/、\、:这类Windows非法字符保存时会直接报错。数据源里“张三/李四”这种值并不算稀奇所以对文件名做一次过滤很有必要import re safe_name re.sub(r[\\/:*?|], _, filename)5. 其他语言和场景的扩展思路5.1 C/C#场景怎么做如果你只能在C环境下做可以使用COM接口调起Excel操作方式和VBA类似生成速度略慢但胜在能直接用现成的Excel文件格式和打印设置。缺点也很明显——目标机器必须安装Office且并发调用COM容易卡死。更轻量一点的做法是生成CSV但CSV不支持多Sheet也不支持模板格式这种情况下模板批量填充基本就做不到了。C# 的话我建议直接选ClosedXML它支持读取现有xlsx模板、替换单元格、保留样式API设计也接近Excel操作习惯。如果你遇到的是“前端传Excel到后端处理”的场景思路依然是一样——IFormFile拿到后先保存到临时文件再交给生成器处理处理完输出到下载目录。5.2 在WPS中用模板批量填充Word热搜词里有“wps2019在excel中批量填充word模板”这个需求其实就是把Excel里的数据批量写到Word模板的占位符里。WPS自带邮件合并功能操作路径大概是“引用-邮件合并-打开数据源”本质上也是“模板数据源”的模式。如果你不想装Office又想自动化可以用Python的docxtpl库操作.docx模板把{{name}}替换成Excel里面的数据再批量输出Word文档。这种场景下前面的模板设计思路完全通用只是占位符语法从{name}换成了{{name}}。5.3 做成通用小工具时的功能边界最后如果这个工具不是只给自己用而是给团队当运维工具我强烈建议把“参数配置从代码里拆出来”。用config.ini或一个独立的setting.json存放模板路径、Sheet名称、输出目录、占位符映射这样别人改配置就能适配新的业务不用碰你的代码。比如我的setting.json长这样{ template_file: template.xlsx, source_file: data.xlsx, output_dir: output, sheet_name: Salary, id_field: 工号, name_field: 姓名, group_field: 部门 }这样做还有一个额外好处后续想加“按部门合并成一个文件”或者“自动发邮件”只需要在配置里加字段不用推翻重写。我在实际使用中还有一个体会这类小工具最难的不是写代码那部分而是“模板怎么定、字段怎么统一、别人改数据时会不会把表头弄乱”。所以模板和源数据的规范文件一定要跟着工具一起发布至少写一个README.md把约束条件写清楚哪些Sheet不能改名、哪些列必须有、占位符怎么用。把这些规范定好了工具才能真正给别人用起来而不是只有自己能维护的“一次性脚本”。本文还有配套的精品资源点击获取
返回列表