在日常开发中,你可能遇到过这种场景:从数据库拉一批数据,填进 Word 模板里,生成几十份合同或报告。手动改?累死人。写个脚本,几分钟搞定。
Python 处理 Word 靠的是 python-docx 这个库。它能读、能写、能改样式,基本上 Word 里常见的操作它都能搞定。
一、模块安装
一条命令搞定:
pip install python-docx
安装完直接导入:
from docx import Documentfrom docx.shared import Pt, RGBColor, Inchesfrom docx.enum.text import WD_ALIGN_PARAGRAPH
这几个导入分别干啥?Document 是核心对象,用来打开和创建文档;Pt 和 RGBColor 控制字号和颜色;WD_ALIGN_PARAGRAPH 控制对齐方式。后面用到再细说。
二、文档读取
拿到一个 Word 文件,第一件事是把它打开:
doc = Document("report.docx")
2.1 读取段落文本
遍历 paragraphs 就能拿到每一段的内容:
for para in doc.paragraphs: print(para.text)
这里有个新手容易踩的坑:para.text 拿到的是纯文本,加粗、斜体这些格式信息会丢失。如果你需要区分哪些文字是加粗的,得往下看 runs 的部分。
2.2 读取表格数据
Word 里的表格存在 doc.tables 列表里,遍历方式如下:
for table in doc.tables: for row in table.rows: row_data = [cell.text for cell in row.cells] print(row_data)
每个 cell 本质上也是一个容器,里面可以有多个段落。上面的 cell.text 会把所有段落拼成一个字符串。
2.3 精确读取格式信息(Runs)
每个段落内部由多个 Run 组成,每个 Run 具有统一的格式:
for para in doc.paragraphs: for run in para.runs: print(f"文本: {run.text}") print(f"加粗: {run.bold}, 斜体: {run.italic}")
举个例子,一段话里"这是重点内容"会被拆成三个 Run:普通"这是"、加粗"重点"、普通"内容"。理解这个模型,后面改样式就不迷糊了。
三、文档写入
3.1 创建新文档并添加内容
from docx import Documentfrom docx.shared import Pt, RGBColorfrom docx.enum.text import WD_ALIGN_PARAGRAPHdoc = Document()# 添加标题(level 1~9 对应一级到九级标题)doc.add_heading("月度数据报告", level=1)# 添加普通段落,支持链式追加格式化文字p = doc.add_paragraph()run1 = p.add_run("报告日期:")run1.bold = Truep.add_run("2024年7月")doc.add_paragraph("本月核心指标如下:", style="List Bullet")doc.save("report_new.docx")
add_paragraph 的 style 参数可以直接用内置样式名,比如 "List Bullet"(无序列表)、"List Number"(有序列表)、"Quote"(引用)。不用手动调缩进。
3.2 创建表格
headers = ["指标", "数值", "环比"]data = [ ["日活", "12,500", "+8%"], ["月活", "58,000", "+3%"], ["转化率", "4.2%", "+0.5%"],]table = doc.add_table(rows=1, cols=len(headers))table.style = "Table Grid"# 填表头for i, h in enumerate(headers): table.rows[0].cells[i].text = h# 填数据行for row_data in data: row = table.add_row() for i, val in enumerate(row_data): row.cells[i].text = val
Table Grid 是带边框的样式,Word 里常用。你也可以试试 "Light Shading Accent 1" 之类的好看样式。
四、批量修改样式
这才是自动化的精髓。从数据库拉数据,写入模板,一键设好所有格式。
4.1 批量设置段落字体和字号
for para in doc.paragraphs: for run in para.runs: run.font.name = "微软雅黑" run.font.size = Pt(12) run.font.color.rgb = RGBColor(0x33, 0x33, 0x33)
上面这段代码把文档里所有文字统一改成微软雅黑、12号、深灰色。在正式报告生成中非常实用。
4.2 设置表格样式
for table in doc.tables: for row in table.rows: for cell in row.cells: for para in cell.paragraphs: para.alignment = WD_ALIGN_PARAGRAPH.CENTER for run in para.runs: run.font.size = Pt(10) run.font.bold = True
先把所有单元格居中、字号设成 10pt、加粗。如果要单独给表头加颜色,可以加个判断行号的逻辑。
python-docx 的核心能力就是这四块:读内容、写内容、改样式、存文件。覆盖了 90% 的 Word 自动化需求。
五、模板填充实战
前面四章都是"从零创建文档",但实际项目里更常见的玩法是模板填充——提前画好 Word 模板,脚本只负责把数据填进去。
5.1 制作模板
先用 Word 手动做一个模板文件 template.docx,内容大致如下:
{{title}}
报告人:{{author}}
报告日期:{{date}}
本月营收:{{revenue}} 万元,环比增长 {{growth_rate}}。
用双花括号 {{xxx}} 做占位符,后面脚本会把这些标记替换成真实数据。
5.2 替换脚本
核心思路:遍历所有段落的所有 Run,逐个检查文本里有没有占位符,有就替换。
from docx import Documentfrom docx.shared import Pt, RGBColordef fill_template(template_path, output_path, data: dict): """读取 Word 模板,替换占位符后另存为新文件""" doc = Document(template_path) # 替换段落中的占位符 for para in doc.paragraphs: for run in para.runs: for key, value in data.items(): placeholder = "{{" + key + "}}" if placeholder in run.text: run.text = run.text.replace(placeholder, str(value)) # 替换表格中的占位符 for table in doc.tables: for row in table.rows: for cell in row.cells: for para in cell.paragraphs: for run in para.runs: for key, value in data.items(): placeholder = "{{" + key + "}}" if placeholder in run.text: run.text = run.text.replace(placeholder, str(value)) doc.save(output_path) print(f"已生成: {output_path}")# === 使用示例 ===report_data = { "title": "2024年7月运营月报", "author": "张三", "date": "2024-07-31", "revenue": "156.8", "growth_rate": "12%",}fill_template("template.docx", "7月月报.docx", report_data)
5.3 批量生成
实际场景是几十个人的合同、几十个门店的报表。加个循环就行:
# 假设从数据库或 Excel 拉了一堆数据employees = [ {"name": "张三", "department": "技术部", "salary": "25,000", "date": "2024-08-01"}, {"name": "李四", "department": "产品部", "salary": "22,000", "date": "2024-08-01"}, {"name": "王五", "department": "设计部", "salary": "20,000", "date": "2024-08-01"},]for emp in employees: data = { "title": f"{emp['name']}的劳动合同", "name": emp["name"], "department": emp["department"], "salary": emp["salary"], "date": emp["date"], } output_file = f"contracts/{emp['name']}_合同.docx" fill_template("contract_template.docx", output_file, data)
一个要注意的坑:Word 的自动更正会把连续输入的文字拆成多个 Run。比如你输入 {{name}},Word 可能把它拆成 {{ 和 name}} 两个 Run,上面的替换代码就匹配不上。
解决方案是在模板里先写好占位符,然后选中整个占位符,统一设置成同一种字体(比如等线),强制它变成单个 Run。如果还是不行,可以写个"Run 合并"函数,把相邻 Run 的文本拼起来检查,替换后再重新分配。