这次我们来做一个 Python 办公自动化实战案例,重点放在Word 文档读写这一块。
课程编号是py100--lv2-085,对应的是 Python 入门 100 天中的进阶第四类任务:用脚本批量生成、修改和转换 Word 文档。实际工作里很多重复劳动都集中在 Word 上,比如每周写周报、批量生成报名表、处理招投标文件里的格式、把 docx 批量转成 PDF。手动做一遍两遍还行,做到第十遍就会想:能不能让 Python 直接干完。
这篇文章不聊概念,直接演示三套能力:用python-docx生成结构化文档、用pywin32调用本地 Word 完成格式转换、用docxcompose或标准库思路做批量合并与模板填充。如果你正在学习 Python 办公自动化、或手头刚好有一批 Word 文档要批量处理,这篇可以直接收藏并按步骤操作。
1. 核心能力速览
在写代码前,先把这节课涉及的技术栈和能力边界讲清楚。
| 能力项 | 说明 |
|---|---|
| 项目类型 | Python 办公自动化实战教程(Word 文档处理方向) |
| 核心依赖 | python-docx、pywin32、pdf2docx、docxcompose |
| 可处理任务 | 新建 word、批量生成 word、修改已有 word、docx 批量转 PDF、PDF 转 docx |
| 推荐环境 | Windows 10/11 + Python 3.9 及以上;macOS/Linux 可运行 python-docx,但 win32com 仅限 Windows |
| 显存需求 | 不需要 GPU,纯 CPU 即可 |
| 启动方式 | 命令行运行 Python 脚本 |
| 是否支持批量任务 | 支持,遍历文件夹即可实现批量处理 |
| 是否提供接口 API | python-docx 是第三方库,可直接集成到 Flask/FastAPI 服务中 |
| 适合人群 | Python 初学者、办公自动化方向学习者、有批量文档处理需求的人 |
注意一点:python-docx不依赖本地安装 Microsoft Word,它是纯 Python 解析.docx文件;而pywin32这种方式必须在本机安装 Word 或 WPS 组件才能调用。实际项目中两种思路经常混用。
2. 适用场景与使用边界
2.1 这个工具适合谁
- 每月要出 30 份格式相同的项目报告,手动改标题、改日期、改姓名,效率太低;
- 人事或教务场景,需要根据 Excel 名单批量生成录用通知书、报名表、成绩单;
- 需要在文档交付前把一批 docx 统一另存为 PDF,并且统一页边距、字体和页码;
- 想把零散文档内容合并到一个 Word 文件里做归档;
- 正在做一件事:避免打开 Word 手动复制粘贴,用几行代码一步到位。
2.2 不适合什么场景
python-docx不是万能的。它不能处理.doc老格式,也不能执行 Word 里复杂的宏逻辑。要解析老式.doc,需要先另存为.docx,或者使用 win32com 调用 Word 完成转换。
另外,如果文档里有复杂的公式、文本框、嵌入对象、复杂的交叉引用,用python-docx修改时容易破坏排版。这种场景更适合用 win32com 在 Word 应用内部操作。
2.3 使用边界与合规提醒
自动化处理 Word 本身没有安全风险,但要注意两点:
- 处理他人提供的文档前,先确认来源合法,涉及个人信息的报名表、成绩单、合同模板要做好脱敏处理;
- 脚本生成的文档如果要商用在外部发布,必须复核内容和格式是否合规;
- 不要编写宏代码去绕过文档保护或密码机制,这属于破坏访问控制,不符合安全边界。
3. 环境准备与前置条件
3.1 安装 Python
到 Python 官网下载 3.9 以上版本,安装时勾选Add Python to PATH。安装完成后,打开终端或命令提示符,输入以下命令验证:
python --version pip --version如果提示命令不存在,说明没有加到 PATH,需要手动把 Python 安装目录加入系统环境变量。
3.2 安装依赖库
本项目核心依赖共四个,按需安装:
pip install python-docx pywin32 pdf2docx docxcompose四个库的作用:
| 库名 | 作用 |
|---|---|
python-docx | 操作 .docx 文件,创建文档、添加段落、表格、图片,修改样式 |
pywin32 | 在 Windows 上调用本机 Word 应用,实现 doc/docx 转 PDF、批量另存为 |
pdf2docx | 将 PDF 文件解析并生成 Word 文档 |
docxcompose | 合并多个 docx 文件,能够保留编号列表和连续页码 |
如果你只在 Linux 服务器上用 python-docx,不需要 pywin32:
pip install python-docx3.3 准备测试文档
建议在本地建一个专门目录:
D:/word_auto/ ├─ input/ # 放待处理的文档 ├─ output/ # 输出结果目录 ├─ template/ # 模板文档目录 └─ scripts/ # Python脚本目录用这个目录规范学习阶段的所有测试。
3.4 验证环境是否正确
安装完成后,先运行一个最小脚本,确认库能正常导入:
from docx import Document doc = Document() doc.add_paragraph("测试段落") doc.save("D:/word_auto/output/test.docx") print("环境正常,文件已生成")如果终端没有报错,说明 python-docx 已经可用了。
4. 创建 Word 文档的三种基础写法
这一节内容是整个实战的根基。先理解 python-docx 最核心的三个对象:Document、Paragraph、Table。
4.1 添加标题与段落
创建文档并设置标题:
from docx import Document from docx.shared import Pt, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH doc = Document() # 添加标题 doc.add_heading("项目周报", level=1) # 添加普通段落 p = doc.add_paragraph("本周完成以下工作:") p.alignment = WD_ALIGN_PARAGRAPH.LEFT # 添加带格式的段落 run = doc.add_paragraph().add_run("这是加粗红色文字") run.bold = True run.font.size = Pt(14) run.font.color.rgb = RGBColor(255, 0, 0) doc.save("D:/word_auto/output/test_format.docx")理解run很重要:它不是整个段落,而是段落中拥有相同格式的一段文本。你要修改某几个字的字体颜色,就操作 run。
4.2 插入表格
创建三行四列的表格,并写入数据:
from docx import Document from docx.shared import Pt doc = Document() table = doc.add_table(rows=3, cols=4) table.style = "Table Grid" data = [ ["姓名", "部门", "本周任务", "状态"], ["张三", "技术部", "完成接口开发", "已完成"], ["李四", "产品部", "输出需求文档", "进行中"] ] for row_idx, row_data in enumerate(data): for col_idx, cell_text in enumerate(row_data): cell = table.cell(row_idx, col_idx) cell.text = str(cell_text) doc.save("D:/word_auto/output/test_table.docx")这里有一个常见坑:如果不指定table.style,表格在生成的 docx 里可能看起来没有边框线,打印或预览时像普通文本。设成"Table Grid"是带全边框的最常用样式。
4.3 插入图片并调整尺寸
from docx import Document from docx.shared import Cm doc = Document() doc.add_heading("图片附页", level=2) pic = doc.add_picture("D:/word_auto/input/chart.png", width=Cm(14)) doc.save("D:/word_auto/output/test_picture.docx")注意:插入图片时传入的是本地路径,图片格式支持 png、jpg、gif 等常见格式。宽度单位使用Cm(14)表示 14 厘米,如果不指定长度,图片按原始像素大小插入,可能超出页面范围。
5. 实战:模板批量生成报名表
进入真正的办公自动化环节。最常见的重复劳动场景是:已经有 Excel 名单,需要给每个人生成一份格式相同的 Word 表。
5.1 需求拆解
- 输入:Excel 名单,包含姓名、性别、部门、职位
- 输出:每人一份 Word 报名表,文件名命名为“姓名_报名表.docx”
- 格式:固定标题、表格有固定表头、个人信息从上到下排列
5.2 打开 Excel 读取名单
先读取 Excel 名单:
import pandas as pd df = pd.read_excel("D:/word_auto/input/roster.xlsx") print(df.head())处理 Excel 时用 pandas 最方便。如果名单量很少,也可以直接用 csv 模块。
5.3 生成报名表主代码
import os from docx import Document from docx.shared import Pt, Cm from docx.enum.table import WD_ALIGN_VERTICAL import pandas as pd def create_registration_form(data: dict, output_path: str): doc = Document() # 文档标题 title = doc.add_heading("报名登记表", level=0) title.alignment = 1 # 居中 # 个人信息表格 table_data = [ ["姓名", data["姓名"], "性别", data["性别"]], ["部门", data["部门"], "职位", data["职位"]], ["联系电话", data["电话"], "邮箱", data["邮箱"]], ["备注", "", "", ""], ] table = doc.add_table(rows=len(table_data), cols=4) table.style = "Table Grid" for row_idx, row_data in enumerate(table_data): for col_idx, cell_text in enumerate(row_data): cell = table.cell(row_idx, col_idx) cell.text = str(cell_text) cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER # 设置列宽 for row in table.rows: row.cells[0].width = Cm(2.5) row.cells[1].width = Cm(4.5) row.cells[2].width = Cm(2.5) row.cells[3].width = Cm(5.5) doc.save(output_path) print(f"已生成: {output_path}") # 主流程 df = pd.read_excel("D:/word_auto/input/roster.xlsx") os.makedirs("D:/word_auto/output/forms", exist_ok=True) for _, row in df.iterrows(): data = { "姓名": row["姓名"], "性别": row["性别"], "部门": row["部门"], "职位": row["职位"], "电话": row["电话"], "邮箱": row["邮箱"], } output_path = f"D:/word_auto/output/forms/{row['姓名']}_报名表.docx" create_registration_form(data, output_path)以上脚本执行后,output/forms 目录下会生成若干 Word 文件。判断标准很简单:文件是否生成、表格里姓名电话是否正确、文件名是否和名单对应。
5.4 增强:自动调整单元格字体
如果希望每个单元格里的字号统一为 11 磅,可以增加单元格遍历逻辑:
for row in table.rows: for cell in row.cells: for paragraph in cell.paragraphs: for run in paragraph.runs: run.font.size = Pt(11)这段逻辑要放在填充表格之后、保存之前。
5.5 注意事项
python-docx 没有内置“查找并替换宏”这种一键操作。当你批量生成时,一定要先拿一个人的数据跑通脚本,再整个文件夹跑,避免生成 50 份错误文件再去改。
6. 修改已有 Word 文档:批量替换文本与样式
实际办公中,很多需求是“把这段文字里的公司名改成另一个公司名”“把日期改成最新日期”“把标题字号统一为 16 磅”。这种场景处理量不大,但手动逐个打开替换同样繁琐。
6.1 批量替换段落文本
python-docx 不能直接做类似 ctrl+H 的全局替换,但可以通过遍历段落中的 run 来实现。这里有三种替换粒度:
- 直接替换段落全部文字,会丢失原有格式;
- 逐个 run 替换精确文本,能保留大部分格式;
- 精确到指定 run 内的文字,通用性最强。
最常用的场景是精确替换段落文本:
from docx import Document def replace_paragraph_text(doc, old_text, new_text): """遍历所有段落,如果段落文本等于旧文本,则替换""" for paragraph in doc.paragraphs: if old_text in paragraph.text: # 保留第一个run的格式,清空其他run for run in paragraph.runs: run.text = "" paragraph.runs[0].text = paragraph.text.replace(old_text, new_text) print(f"替换段落: {old_text} -> {new_text}") doc = Document("D:/word_auto/input/old_report.docx") replace_paragraph_text(doc, "北京某某科技有限公司", "上海某某信息科技有限公司") doc.save("D:/word_auto/output/new_report.docx")注意这里的坑:如果直接把paragraph.text用字符串方法替换,虽然看似只改文字,但原来这段里被切分成多个 run 的加粗、颜色信息会丢失。把旧文本所在的每个 run 分别替换才更安全。
6.2 精确到 run 的替换函数
为了保留被替换部分周围的格式,可以改进成逐 run 匹配:
from docx import Document def replace_in_runs(doc, old_text, new_text): count = 0 for paragraph in doc.paragraphs: for run in paragraph.runs: if old_text in run.text: run.text = run.text.replace(old_text, new_text) count += 1 # 表格中的段落也要处理 for table in doc.tables: for row in table.rows: for cell in row.cells: for p in cell.paragraphs: for run in p.runs: if old_text in run.text: run.text = run.text.replace(old_text, new_text) count += 1 print(f"共替换 {count} 处") return doc这个函数是对 word 自动化很有用的一个工具函数,可以放在自己的公共模块里。
6.3 修改表格样式与列宽
处理批量表格的格式时,需要遍历每个表格的每一行。
from docx import Document from docx.shared import Cm doc = Document("D:/word_auto/input/need_change.docx") for table in doc.tables: # 设置所有列宽 for row in table.rows: row.cells[0].width = Cm(2) row.cells[1].width = Cm(6) # 表头加粗 for cell in table.rows[0].cells: for paragraph in cell.paragraphs: for run in paragraph.runs: run.bold = True doc.save("D:/word_auto/output/table_changed.docx")热词里提到“poi设置word表格单元格宽度”,对应到 Java 是 POI,Python 里调整列宽就是通过row.cells[i].width设置。要注意 Word 实际渲染时会综合每列所有单元格的宽度,最好统一设置每一列的单元格宽度。
6.4 批量操作文件夹中的所有 docx
实现批量任务的核心是遍历文件夹:
import os from docx import Document input_dir = "D:/word_auto/input" output_dir = "D:/word_auto/output" for filename in os.listdir(input_dir): if filename.lower().endswith(".docx"): input_path = os.path.join(input_dir, filename) output_path = os.path.join(output_dir, filename) doc = Document(input_path) replace_in_runs(doc, "腾讯", "某某企业") doc.save(output_path) print(f"处理完成: {filename}")批量任务务必加日志,把处理过的文件名打印出来,方便排查。
7. win32com:调用 Word 完成 doc/docx 互转与 PDF 输出
python-docx 是“不启动 Word 应用”地解析文件,功能局限在于无法处理.doc老格式、无法读取页面设置里的某些复杂属性。如果需要调用 Word 本身的另存为、打印、合并功能,在 Windows 上可以用 pywin32 连接 COM 组件。
7.1 win32com 的适用场景
- 把
.doc转成.docx,再用 python-docx 后续处理; - 把 docx 批量转 PDF;
- 打开文档后调整页边距、页眉页脚;
- 获取 Word 文档真实的页数,按页拆分文档;
- 执行 Word 自带的查找替换功能。
7.2 单文件 docx 转 PDF
import os from win32com.client import DispatchEx def docx_to_pdf(input_docx, output_pdf): # DispatchEx 比 Dispatch 更安全,避免引用已有实例 word = DispatchEx("Word.Application") word.Visible = False # 不显示Word界面 word.DisplayAlerts = 0 # 不显示弹窗 try: doc = word.Documents.Open(os.path.abspath(input_docx)) doc.SaveAs(os.path.abspath(output_pdf), FileFormat=17) # 17 是 PDF 格式代码 doc.Close() print(f"转换成功: {output_pdf}") except Exception as e: print(f"转换失败: {e}") finally: word.Quit()调用示例:
docx_to_pdf("D:/word_auto/input/old_report.docx", "D:/word_auto/output/old_report.pdf")注意:FileFormat=17是 Word 的 PDF 导出格式代码,这个值是稳定的,不会变。
7.3 批量转换文件夹内所有 Word 文档为 PDF
import os from win32com.client import DispatchEx input_dir = "D:/word_auto/input" output_dir = "D:/word_auto/output/pdf_batch" os.makedirs(output_dir, exist_ok=True) word = DispatchEx("Word.Application") word.Visible = False word.DisplayAlerts = 0 try: for filename in os.listdir(input_dir): if filename.lower().endswith((".docx", ".doc")): input_path = os.path.abspath(os.path.join(input_dir, filename)) base_name = os.path.splitext(filename)[0] output_path = os.path.join(output_dir, base_name + ".pdf") doc = word.Documents.Open(input_path) # 统一去除只读或修改权限限制 doc.ReadOnlyRecommended = False doc.SaveAs(os.path.abspath(output_path), FileFormat=17) doc.Close() print(f"已转换: {filename}") finally: word.Quit()7.4 win32com 的批量处理注意事项
每个文件转换之间留出时间:批量转换十几份没问题,上百份时 Word COM 进程可能卡死。建议在每份文档处理后加入:
import time time.sleep(0.5)观察任务管理器:Win32com 方式会启一个WINWORD.EXE进程。如果脚本异常退出,这个进程可能残留,导致后续文件打开时提示“文件正在使用”。遇到这种情况,在任务管理器中结束WINWORD.EXE或多出的 Python 进程即可。
7.5 用 win32com 清空页眉页脚或重置样式
在某些版本更新或模板切换场景,旧的页眉页脚需要统一清理。COM 方式路径很简单:
from win32com.client import DispatchEx word = DispatchEx("Word.Application") word.Visible = False doc = word.Documents.Open("D:/word_auto/input/old_header.docx") for section in doc.Sections: header = section.Header header.Range.Delete() doc.Save() doc.Close() word.Quit()这里不建议用 python-docx 处理页眉,不是做不到,而是页眉在不同节之间链接关系比较复杂,COM 操作更符合人类使用 Word 的逻辑。
8. PDF 转 Word:使用 pdf2docx
热词中“pdf转word”出现频率很高,正好补充这一节。
pdf2docx可以把 PDF 文件解析并转成 Word 文档,适合处理文字型和简单表格型 PDF,不适合扫描件(扫描件需要 OCR 配合)。
8.1 单文件转换
from pdf2docx import Converter pdf_file = "D:/word_auto/input/paper.pdf" docx_file = "D:/word_auto/output/paper.docx" cv = Converter(pdf_file) cv.convert(docx_file) cv.close() print("转换完成")8.2 批量转换目录下所有 PDF
import os from pdf2docx import Converter input_dir = "D:/word_auto/input" output_dir = "D:/word_auto/output/docx_from_pdf" os.makedirs(output_dir, exist_ok=True) for filename in os.listdir(input_dir): if filename.lower().endswith(".pdf"): pdf_path = os.path.join(input_dir, filename) docx_name = os.path.splitext(filename)[0] + ".docx" docx_path = os.path.join(output_dir, docx_name) cv = Converter(pdf_path) cv.convert(docx_path, start=0, end=None) cv.close() print(f"已转换: {filename}")8.3 当前转换局限
PDF 转 Word 后的排版能否保持完美,主要看源 PDF 是否使用嵌入字体、是否有复杂的图文混排。如果 PDF 里只有清晰文字,转换结果很好;如果含有大量图片、旋转文字、艺术字,转换后需要校对版面。
重要提醒:只转换你自己拥有版权或获准使用的 PDF 文件,不要拿自动化工具批量处理他人受版权保护的书籍和论文。
9. 批量合并多个 Word 文档
如果多个 word 章节文件需要合成一个新的 word 文件,可以用 docxcompose 完成。
from docxcompose.composer import Composer from docx import Document master_doc = Document("D:/word_auto/input/chapter_1.docx") composer = Composer(master_doc) files_to_append = [ "D:/word_auto/input/chapter_2.docx", "D:/word_auto/input/chapter_3.docx", ] for file_path in files_to_append: append_doc = Document(file_path) composer.append(append_doc) composer.save("D:/word_auto/output/merged.docx")使用 docxcompose 的好处是它会尽量保留原文档的编号连续性和分页符处理,比直接用 python-docx 的底层 body XML 合并靠谱。
如果只需要简单地把几个文档的内容堆到一个文档里,也可以自己写纯 python-docx 版本,但处理页码格式、列表编号时会比较麻烦。工程上优先推荐 docxcompose。
10. 资源占用与性能观察
这一节重点回答“脚本跑的时候占多少资源、会不会卡顿”。
10.1 python-docx 的资源占用
python-docx不启动 Word 进程,它以纯 Python 方式读取 zip 包中的 XML,因此性能瓶颈集中在 CPU 和内存上。生成一份 10 页、带 5 个表格的文档,内存占用通常在几十到几百 MB 之间,具体看图片数量。批量处理几千个文件时,内存会累积,建议每处理完一个文件后调用 save,并让变量回收。
10.2 win32com 的资源占用
win32com 启动 Word 应用,会看到WINWORD.EXE进程,内存一般在 100 到 300 MB 左右。这个不是 Python 能直接控制的,它属于 Word 本身的资源占用。批量转换多份文档时,如果发现 Word 进程占用持续飙高,可以每处理完 N 份后重启一次 Word 进程。
处理大量带图片的 docx 时,优先推荐纯 python-docx 处理,不启动 Word,速度更快。
10.3 PDF 转换的资源占用
pdf2docx 是 CPU 密集任务。页数越多、图片越多,耗时越长。转换一本 200 页的 PDF 可能需要几十秒甚至几分钟,不是脚本卡死,是计算量确实大。遇到大文件先转 5 页测试,确认排版符合预期再全量转。
10.4 降低资源占用的通用手段
- 减少不必要的循环嵌套;
- 每处理一个文件释放一次引用;
- 在循环中不要频繁打开同一个文档;
- 批量任务用日志记录进度,方便中途中断后继续;
- 大批量处理时不要一次性把所有文件读入内存列表,使用
os.scandir或生成器逐条遍历。
11. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
导入docx报 ModuleNotFoundError | python-docx 未安装 | 执行pip list查看依赖 | 执行pip install python-docx |
| 生成的 Word 打开提示“文件已损坏” | 文档被错误路径保存,或 Word 正在占用源文件 | 查看保存路径和权限 | 在非系统目录路径下重新保存;关闭 Word 中的同名文件 |
python-docx 打开.doc失败 | 库不支持.doc老格式 | 检查文件后缀 | 用 win32com 将.doc转.docx |
| 表格添加后预览没有边框 | 未指定表格样式 | 检查代码是否设置table.style | 添加table.style = "Table Grid" |
| 替换文字后格式丢失 | 对整个段落文本重新赋值导致 run 被合并 | 检查替换逻辑 | 改为在 run 级别替换,保留格式 |
win32com 找不到Word.Application | 没有安装 Word/WPS,或 COM 注册失败 | 检查电脑是否安装 Office | 安装 WPS 或 Office;确认 Office 是完整安装而非商店精简版 |
| 批量转换时卡在某个文件 | 个别文档有破损或复杂对象 | 查看日志停在哪一个文件 | 先跳过该文件,手动打开另存一次后重新处理 |
| 生成的 PDF 页数和 Word 不一致 | 字体缺失导致分页变化 | 检查系统中文字体安装情况 | 安装常用中文字体;在 Word 中设置默认字体后重新转换 |
pdf2docx 转换时抛ImportError | PDF 里的对象无法解析 | 查看错误具体页面 | 对问题 PDF 做 OCR 预处理或调整转换页码范围 |
| 脚本运行后看不到输出 | 终端缓冲或 print 被吞 | 重定向输出到日志文件 | 使用logging模块输出到文件 |
排查流程一般按顺序做:确认 Python 版本与依赖、确认文件路径、确认文档格式、看报错堆栈、单文件调试、再批量执行。
排查的第一行代码永远是先打印路径:
import os print(os.path.exists("D:/word_auto/input/old_report.docx")) print(os.path.abspath("D:/word_auto/input/old_report.docx"))不要假设路径对,直接把路径是否存在打出来是最快的排查方式。
12. 一键封装为带界面的小工具
热词中大量出现“python 转 exe 文件”,这说明很多读者不只想要脚本,而是想给同事做一个 windows 双击能运行的批量转换工具。这里给一个轻量方案。
12.1 用 Tkinter 写最小界面
Python 自带 Tkinter,不需要额外库:
import tkinter as tk from tkinter import filedialog, messagebox import os from win32com.client import DispatchEx def select_folder(): folder = filedialog.askdirectory() entry_path.delete(0, tk.END) entry_path.insert(0, folder) def convert_now(): folder = entry_path.get() if not folder: messagebox.showwarning("提示", "请先选择文件夹") return word = DispatchEx("Word.Application") word.Visible = False os.makedirs(os.path.join(folder, "pdf输出"), exist_ok=True) try: for filename in os.listdir(folder): if filename.lower().endswith((".docx", ".doc")): input_path = os.path.join(folder, filename) output_name = os.path.splitext(filename)[0] + ".pdf" output_path = os.path.join(folder, "pdf输出", output_name) doc = word.Documents.Open(os.path.abspath(input_path)) doc.SaveAs(os.path.abspath(output_path), FileFormat=17) doc.Close() messagebox.showinfo("完成", "批量转换完成") finally: word.Quit() app = tk.Tk() app.title("Word批量转PDF工具") app.geometry("480x160") tk.Label(app, text="选择文件夹:").pack(pady=5) entry_path = tk.Entry(app, width=50) entry_path.pack(pady=5) btn_choose = tk.Button(app, text="选择文件夹", command=select_folder) btn_choose.pack(pady=5) btn_convert = tk.Button(app, text="开始批量转换", command=convert_now) btn_convert.pack(pady=10) app.mainloop()12.2 打 exe
pip install pyinstaller pyinstaller -F -w word_to_pdf.py生成的 exe 在dist目录中。注意:这种 exe 需要在装有 Word/WPS 的电脑上运行,因为 win32com 是调用本机 Office 组件的。如果要脱离 Office 处理 docx 的生成工作,不要使用 pywin32,改用纯 python-docx 实现,那样的 exe 可以独立运行。
13. 超实用改造:接入 API 提供文档处理服务
办公自动化到后期,都会把重复操作封装成 HTTP 接口,方便前端或其他系统调用。
13.1 创建 FastAPI 文档生成服务
先安装 FastAPI 和 uvicorn:
pip install fastapi uvicorn python-multipart创建一个接口服务,接收 JSON 数据,生成 word 文档并返回下载:
from fastapi import FastAPI from fastapi.responses import FileResponse from pydantic import BaseModel from docx import Document import os import uuid app = FastAPI() class ReportData(BaseModel): username: str department: str week: str tasks: list[str] @app.post("/api/generate_word") def generate_word(data: ReportData): doc = Document() doc.add_heading("周报 — " + data.username, level=1) doc.add_paragraph("部门:" + data.department) doc.add_paragraph("周次:" + data.week) doc.add_heading("本周工作", level=2) for task in data.tasks: doc.add_paragraph(task, style="List Bullet") output_dir = "D:/word_auto/api_output" os.makedirs(output_dir, exist_ok=True) file_name = f"{uuid.uuid4().hex}.docx" file_path = os.path.join(output_dir, file_name) doc.save(file_path) return FileResponse( file_path, filename=f"{data.username}_周报.docx", media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document" ) if __name__ == "__main__": import uvicorn uvicorn.run("main:app", host="127.0.0.1", port=8000)13.2 调用接口示例
启动服务后,用 curl 或 requests 调用:
curl -X POST "http://127.0.0.1:8000/api/generate_word" ^ -H "Content-Type: application/json" ^ -d "{\"username\":\"王五\",\"department\":\"技术部\",\"week\":\"第3周\",\"tasks\":[\"任务A\",\"任务B\"]}" ^ -o test_report.docxPython 调用:
import requests url = "http://127.0.0.1:8000/api/generate_word" payload = { "username": "王五", "department": "技术部", "week": "第3周", "tasks": ["完成API开发", "编写测试文档"] } resp = requests.post(url, json=payload) with open("output.docx", "wb") as f: f.write(resp.content)服务一旦跑通,就可以嵌入到 OA 系统里,让前端按钮直接触发 word 文档生成下载,不需要人工复制粘贴。
13.3 接口服务的边界说明
接口服务一旦开启,允许别人通过 POST 请求生成文档,必须考虑:
- 部署在内网或绑定 127.0.0.1,避免外部用户无限制调用;
- 为接口增加认证鉴权;
- 设置上传大小限制;
- 如果生成文档内容包含用户隐私数据,输出文件需要设为临时文件,定时清理。
14. 常见操作细节与坑位清单
这一节把实战中容易出问题的点集中列出,帮助少走弯路。
14.1 中文字体设置
默认生成的 docx 使用 Word 的默认字体。在中文 Windows 上通常是等线或宋体,但如果需要在 Linux 服务器上生成,系统没有对应中文字体会导致 Word 打开时字体替换。更稳妥的方法是直接指定中文字体:
from docx import Document from docx.shared import Pt from docx.oxml.ns import qn doc = Document() style = doc.styles["Normal"] style.font.name = "微软雅黑" style._element.rPr.rFonts.set(qn("w:eastAsia"), "微软雅黑") style.font.size = Pt(11) doc.add_paragraph("中文字体测试") doc.save("D:/word_auto/output/font_test.docx")必须同时设置w:eastAsia字体属性,否则中文字体名称不会生效。
14.2 段落对齐
python-docx 设置居中:
from docx.enum.text import WD_ALIGN_PARAGRAPH p = doc.add_paragraph() p.alignment = WD_ALIGN_PARAGRAPH.CENTER14.3 插入分页符
from docx.enum.text import WD_BREAK p = doc.add_paragraph() run = p.add_run() run.add_break(WD_BREAK.PAGE)14.4 获取 docx 文件里的所有文本
调试阶段经常要打印文档内容。用 python-docx 遍历段落表格:
from docx import Document doc = Document("D:/word_auto/input/test.docx") for p in doc.paragraphs: print(p.text) for table in doc.tables: for row in table.rows: for cell in row.cells: print(cell.text)如果不输出任何结果,可能是表格内容嵌在文本框或 SmartArt 中,需要在 Word 里手动检查。
15. 最佳实践与工程化建议
15.1 文档模板与代码分离
批量生成 word 文档时,不要把样式写死在大量 Python 代码中。更好的做法是准备一个.docx模板,在里面写好“占位符”,用 Python 扫描并替换占位符。
例如模板里写{姓名}、{部门},代码里做替换:
def fill_template(template_path, output_path, values: dict): from docx import Document doc = Document(template_path) for paragraph in doc.paragraphs: for key, value in values.items(): placeholder = "{" + key + "}" if placeholder in paragraph.text: # 工作区逐 run 替换 pass for table in doc.tables: for row in table.rows: for cell in row.cells: for key, value in values.items(): placeholder = "{" + key + "}" for p in cell.paragraphs: if placeholder in p.text: p.text = p.text.replace(placeholder, str(value)) doc.save(output_path)模板化最大的好处是:同事或业务方可以直接改 docx 里的样式,不需要重新交付 Python 代码。
15.2 批量任务要留日志
批量处理几十上百个文件时,脚本崩溃后要知道卡在哪个文件。加日志的第一选择是标准库 logging,而不是到处都是 print。
import logging logging.basicConfig( filename="D:/word_auto/logs/batch.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", encoding="utf-8" ) logging.info("开始处理: report_001.docx") logging.error("处理失败: report_003.docx")15.3 先用小样本试跑
不要一开始就跑全量。复制两三个文件到 sample 目录,先验证脚本逻辑,确认输出打开后排版正确,再执行整目录批量处理。
15.4 目录结构固定
建议在任何项目中都保持固定目录:
project/ ├─ data/input/ # 原始文件 ├─ data/output/ # 输出文件 ├─ data/backup/ # 备份原始文件 ├─ scripts/ # Python脚本 └─ logs/ # 日志脚本使用pathlib而不是手写字符串拼接路径,避免路径分隔符在不同操作系统上的差异:
from pathlib import Path input_dir = Path("D:/word_auto/input") output_dir = Path("D:/word_auto/output") output_dir.mkdir(parents=True, exist_ok=True)15.5 涉及第三方文档的合规要求
如果你是处理公司内部资料、客户合同、他人简历、或者任何含个人信息的文档,必须确认处理行为在授权范围内;测试数据要用自己生成的假数据,不要用真实身份证号、手机号跑测试脚本。
16. 思考:从单脚本到办公室自动化平台
回到项目标题python办公自动化word-doc。当前课程水平对应的不仅是“会用某个库”,而是建立一套处理文档的流程:
- 明确任务类型:生成新文档、修改已有文档、批量格式转换、内容抽取;
- 选择工具链:python-docx 用于纯代码读写,win32com 用于和 Word 应用交互,pdf2docx 用于 PDF 反向转出;
- 封装成函数:每个功能做成独立函数,输入路径输出路径都用参数化;
- 测试与备份:先小样本测试,任何批量修改之前备份一份原始目录;
- 自动化调度:如果每周都要执行一次,可以把脚本挂到 Windows 任务计划程序里定时运行。
如果这套脚本被多个同事使用,下一步自然是做成 web 服务或 exe 小工具。这也就是很多 python 办公自动化训练营最终要完成的目标。
文档处理类任务的难点不是某个 API 记不住,而是格式细节太多了。先跑通逻辑,再迭代排版细节,会顺利很多。
后面的学习中,可以继续看 python-docx 官方文档、python-pptx 用于 PPT 自动化、openpyxl 用于 Excel 自动化。热词里“python办公自动化excel篇”“pdf转word”“python量化交易策略代码”这些方向,本质上都是同一个套路:读取结构化数据、填充模板、批量输出、转换格式。
如果时间有限,优先把本节中的replace_in_runs、表格填充、模板化三个函数存入自己的工具库,它们的使用频率会非常高。