第338讲:分别用 VBA 和 Python(python-docx) 来实现:Excel数据批量生成Word文档(合同/工单)
场景:从员工信息表批量生成入职合同或工单
在日常办公中,HR、行政、运营同学最常遇到的一个痛点就是:Excel里有一堆数据,却要一个个复制到Word模板里生成合同、工单、通知书。手工操作不仅慢,还容易出错。很多人知道可以用Word的“邮件合并”,但一旦模板复杂(比如带条件判断、动态表格、不同条款),邮件合并就显得力不从心。
今天这讲,我会从实战角度,分别用 VBA 和 Python(python-docx) 来实现“从Excel批量生成Word文档”。不只告诉你怎么做,还会对比两者背后的对象模型差异,让你真正理解“什么时候该用VBA,什么时候该用Python”。
一、典型业务场景说明
假设你是HR,手上有这样一张Excel员工信息表(employee.xlsx):
工号 | 姓名 | 部门 | 岗位 | 入职日期 | 合同期限(年) | 月薪 |
|---|
1001 | 张三 | 技术部 | 后端工程师 | 2025-03-01 | 3 | 18000 |
1002 | 李四 | 产品部 | 产品经理 | 2025-03-05 | 2 | 16000 |
你需要根据这个表,批量生成入职劳动合同(Word版),每个员工一个文件,命名规则为:工号_姓名_劳动合同.docx。
Word模板(contract_template.docx)中有若干占位符,例如:
员工姓名:{{姓名}}
所属部门:{{部门}}
合同期限:{{合同期限}}年
……
下面我们分别用两种技术方案实现。
二、VBA方案:Word.Application + 书签替换
1. 为什么用“书签”而不是“查找替换”
很多VBA初学者会用 Selection.Find.Execute做全文替换,但在正式合同中不推荐,原因有三:
误伤风险:如果正文里恰好出现和占位符相同的文字,会被错误替换。
格式不稳定:查找替换容易打乱原有字体、加粗、段落格式。
无法精确定位:合同往往有多个章节,需要精确控制位置。
书签(Bookmark)是Word对象模型中专门用于“标记位置”的对象,非常适合做结构化填充。
2. Word模板准备(关键一步)
在Word模板中:
选中要填充的位置(如“张三”处)。
「插入」→「书签」。
输入书签名,如 bm_name、bm_department。
注意:书签名不能重复,不能有空格。
一个规范的合同模板,书签设计通常如下:
书签名 | 含义 |
|---|
bm_id | 工号 |
bm_name | 姓名 |
bm_dept | 部门 |
bm_position | 岗位 |
bm_start | 入职日期 |
bm_years | 合同期限 |
bm_salary | 月薪 |
3. VBA代码实现(逐行注释)
在Excel中按 Alt + F11,插入 → 模块,粘贴以下代码:
Sub BatchGenerateContracts() Dim wdApp As Object Dim wdDoc As Object Dim wb As Workbook Dim ws As Worksheet Dim lastRow As Long Dim i As Long Dim templatePath As String Dim savePath As String ' 模板路径和保存路径(务必修改成你自己的) templatePath = "C:\Templates\contract_template.docx" savePath = "C:\Contracts\" ' 当前工作簿和工作表 Set wb = ThisWorkbook Set ws = wb.Sheets("Sheet1") ' 创建Word应用 On Error Resume Next Set wdApp = CreateObject("Word.Application") If wdApp Is Nothing Then MsgBox "无法创建Word应用,请确认已安装Microsoft Word。" Exit Sub End If On Error GoTo 0 wdApp.Visible = False ' 后台运行,速度快 ' 找到最后一行 lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row ' 遍历每一行员工数据 For i = 2 To lastRow ' 打开模板 Set wdDoc = wdApp.Documents.Open(templatePath) ' 替换书签内容 With wdDoc.Bookmarks .Item("bm_id").Range.Text = ws.Cells(i, 1).Value .Item("bm_name").Range.Text = ws.Cells(i, 2).Value .Item("bm_dept").Range.Text = ws.Cells(i, 3).Value .Item("bm_position").Range.Text = ws.Cells(i, 4).Value .Item("bm_start").Range.Text = Format(ws.Cells(i, 5).Value, "yyyy年mm月dd日") .Item("bm_years").Range.Text = ws.Cells(i, 6).Value .Item("bm_salary").Range.Text = ws.Cells(i, 7).Value End With ' 保存为新文件 wdDoc.SaveAs2 savePath & ws.Cells(i, 1).Value & "_" & ws.Cells(i, 2).Value & "_劳动合同.docx" ' 关闭当前文档,不保存模板 wdDoc.Close SaveChanges:=False Next i ' 退出Word wdApp.Quit Set wdDoc = Nothing Set wdApp = Nothing MsgBox "批量生成完成!共生成 " & lastRow - 1 & " 份合同。"End Sub
4. VBA方案的优缺点总结
优点:
无需安装额外环境,Office自带。
对Word格式控制极其精细(字体、段落、表格、页眉页脚)。
适合在纯Windows + Office环境下的企业内部使用。
缺点:
只能在Windows运行,Mac版Excel对VBA支持不完整。
代码可维护性一般,复杂逻辑写起来冗长。
对非技术人员不友好,“宏被禁用”是常见问题。
三、Python方案:python-docx 库 + 占位符替换
当你面对的是跨平台、自动化流水线,或者模板逻辑较复杂时,Python是更好的选择。
1. 环境准备
pip install python-docx pandas openpyxl
python-docx:操作Word文档
pandas:读取Excel
openpyxl:Excel引擎
2. Word模板设计(占位符法)
不同于VBA的书签,Python-docx对书签支持并不友好,业界常用做法是占位符替换。
在Word模板中使用双大括号占位符:
员工姓名:{{name}}
所属部门:{{dept}}
合同期限:{{years}}年
月薪:{{salary}}元
这种写法直观、易维护,也方便非程序员理解。
3. Python核心实现代码
import pandas as pdfrom docx import Documentimport osfrom datetime import datetime# 路径配置EXCEL_PATH = r"C:\data\employee.xlsx"TEMPLATE_PATH = r"C:\templates\contract_template.docx"SAVE_DIR = r"C:\contracts"# 读取Exceldf = pd.read_excel(EXCEL_PATH)# 确保保存目录存在os.makedirs(SAVE_DIR, exist_ok=True)def replace_text_in_paragraph(paragraph, placeholders): """ 替换单个段落中的占位符 """ for key, value in placeholders.items(): if f"{{{{{key}}}}}" in paragraph.text: # 保留原有格式 inline = paragraph.runs for run in inline: run.text = run.text.replace(f"{{{{{key}}}}}", str(value))def generate_contract(row): """ 生成单个合同 """ doc = Document(TEMPLATE_PATH) # 构造占位符字典 placeholders = { "id": str(row["工号"]), "name": row["姓名"], "dept": row["部门"], "position": row["岗位"], "start": row["入职日期"].strftime("%Y年%m月%d日"), "years": str(row["合同期限(年)"]), "salary": f"{row['月薪']:,}" } # 遍历所有段落 for para in doc.paragraphs: replace_text_in_paragraph(para, placeholders) # 处理表格中的占位符(合同常包含表格) for table in doc.tables: for row_table in table.rows: for cell in row_table.cells: for para in cell.paragraphs: replace_text_in_paragraph(para, placeholders) # 保存文件 filename = f"{row['工号']}_{row['姓名']}_劳动合同.docx" filepath = os.path.join(SAVE_DIR, filename) doc.save(filepath) print(f"已生成:{filename}")# 批量生成for idx, row in df.iterrows(): generate_contract(row)print("全部合同生成完成。")
4. Python方案的进阶技巧
(1)条件条款渲染
有些合同条款是有条件的,例如:
if row["合同期限(年)"] >= 3: placeholders["clause"] = "本合同包含长期服务奖金条款。"else: placeholders["clause"] = ""
(2)动态表格行
python-docx支持动态添加表格行,非常适合工单、明细表:
table = doc.tables[0]new_row = table.add_row()new_row.cells[0].text = "加班费"new_row.cells[1].text = "2000"
(3)与自动化流程集成
Python脚本可以轻松接入:
Windows任务计划程序
Jenkins / GitLab CI
企业微信机器人回调
这是VBA难以做到的。
5. Python方案的优缺点总结
优点:
跨平台(Windows / macOS / Linux)。
代码可读性强,逻辑清晰,易维护。
可结合Pandas做复杂数据处理。
易于与企业自动化系统对接。
缺点:
需要安装Python环境(对非技术团队有门槛)。
对Word高级格式(如文本框、页眉页脚深层嵌套)支持不如VBA直接。
不支持直接调用Word“邮件合并”引擎。
四、核心对照:python-docx vs VBA Word对象模型
这是本讲的精华部分,建议反复阅读。
维度 | VBA + Word对象模型 | Python + python-docx |
|---|
运行环境 | Windows + Office | 跨平台 + Python |
模板机制 | 书签(Bookmark) | 占位符({{key}}) |
格式控制 | ⭐⭐⭐⭐⭐(原生支持) | ⭐⭐⭐⭐(依赖run层级) |
表格操作 | 非常成熟 | 成熟,但略繁琐 |
条件逻辑 | 支持,但语法冗长 | 支持,Python原生逻辑清晰 |
错误处理 | On Error Resume Next | try/except,结构清晰 |
自动化集成 | 弱(依赖Office) | 强(命令行、API、CI) |
学习曲线 | 对Office用户友好 | 对开发者友好 |
一句话总结:
**如果你在Windows上,只和Word打交道,选VBA;
如果你要做自动化、跨平台、复杂逻辑,选Python。**
五、实战选型建议
在实际工作中,我通常这样建议团队:
行政/HR单机使用:
用VBA,一键按钮搞定,不需要IT介入。
IT/数据分析团队:
用Python,方便版本管理、复用和自动化。
混合模式(推荐):
Excel作为前端录入界面,Python作为后台引擎,通过“生成按钮 → 调用Python脚本”的方式结合两者优势。
六、常见坑位提醒
VBA书签删除后不会自动恢复:替换一次后书签会消失,若需多次使用,需复制模板再操作。
python-docx不能直接编辑页眉页脚中的占位符:需要单独遍历 doc.sections。
路径问题:Windows路径建议加 r""原始字符串,避免转义错误。
Excel日期类型:Python读取后要显式 .strftime(),否则可能变成浮点数。
七、练习题(巩固本讲内容)
第338讲课后练习(选择题)
在VBA批量生成Word文档时,相比“查找替换”,使用“书签(Bookmark)”的主要优势不包括以下哪项?
A. 精确定位填充位置
B. 避免误替换正文相似文字
C. 可以直接调用Python脚本
D. 更好保留原有格式
使用python-docx进行占位符替换时,为什么通常需要遍历 paragraph.runs?
A. runs代表不同的页面
B. runs保存了字符级格式信息,直接替换paragraph.text会丢失格式
C. runs是Word中的图片对象
D. runs等同于书签
以下关于VBA方案的说法,正确的是?
A. VBA代码可以在macOS版Excel中无缝运行
B. VBA可以直接操作Word的所有对象模型
C. VBA不适合任何批量文档生成场景
D. VBA无需任何模板即可生成格式复杂的合同
如果一个Word合同模板中包含多个表格,Python代码需要如何处理才能替换表格内的占位符?
A. 只需替换paragraphs即可
B. 必须遍历doc.tables,并逐个cell、paragraph处理
C. python-docx不支持表格操作
D. 只能通过VBA实现
在企业自动化场景中,Python方案相比VBA的最大优势是?
A. 不需要安装任何软件
B. 更容易与CI/CD、定时任务、API系统集成
C. 比VBA运行速度更快
D. 可以直接修改Excel公式
八、参考答案
C
B
B
B
B