正确做法是显式调用Document底层构造器拷贝模板body;替换{xxx}需遍历runs定位并保留格式;填表格应复用原段落;保存时避免中文路径或宏模板。

python-docx 打开模板后内容消失?
不是 bug,是 Document 初始化方式错了。直接用 Document("template.docx") 会清空原有内容——它默认创建新文档,路径参数只在“无参时尝试加载”这个特殊逻辑里起作用。
正确做法是显式调用 Document 的底层构造器:
from docx import Document
doc = Document() # 先建空文档
doc._body.clear_content() # 清掉默认段落
doc._element.body._new_body().append(
Document("template.docx")._element.body # 把模板 body 拷贝进来
)
更稳妥的替代方案是用 zipfile 直接解压模板、替换 document.xml 中的占位符再重打包,但仅限纯文本填充场景;含表格/图片/样式时仍推荐上面的 body 拷贝法。
怎么安全替换模板里的 {xxx} 占位符?
别用 str.replace() 或正则全局替换——python-docx 的文本可能分散在多个 run 里,跨 run 的占位符会被切开,直接字符串操作会漏掉或错位。
必须遍历所有段落和表格单元格,逐个检查 paragraph.text 是否含占位符,再定位到具体 run 替换:
- 先用
paragraph.text.find("{name}")判断是否存在 - 再遍历
paragraph.runs,累计run.text长度,找到占位符落在哪个run及起止位置 - 用
run.text.replace("{name}", "张三"),并保留原run的字体/加粗等格式
注意:表格嵌套单元格、文本框、页眉页脚里的内容需单独处理,doc.paragraphs 和 doc.tables 不覆盖这些区域。
填充表格后格式全乱了?
常见于从模板复制表格再填数据——table.cell(row, col).text = "xxx" 看似简单,但会重置该单元格所有段落样式,包括缩进、对齐、行高。
真正要保留模板样式,得复用原单元格里的第一个段落:
cell = table.cell(0, 0)
if cell.paragraphs:
p = cell.paragraphs[0]
p.clear() # 清空但不删段落对象
p.add_run("新内容")
else:
cell.text = "新内容" # 降级兜底
如果模板表格用了合并单元格(ct_merge),直接赋值 cell.text 会触发 InvalidRequestError,必须先检查 cell._tc.tcPr 是否含 gridSpan 或 vMerge 属性,有则跳过或手动拆分。
生成的 DOCX 在 Word 里提示“文件已损坏”?
大概率是中文路径或文件名含空格/特殊字符,Windows 下 python-docx 用 zipfile 打包时没做编码转义,导致 ZIP 中心目录记录的文件名乱码。
解决方法只有两个:
- 保存时强制用英文路径:
doc.save("report_output_v2.docx"),绝对不要用os.path.join("输出", "报告.docx") - 若必须中文路径,先用
tempfile.mktemp(suffix=".docx")写入临时文件,再shutil.move()到目标位置
另一个隐蔽原因是模板本身带宏或受保护,python-docx 无法识别 vbaProject.bin,强行写入会破坏 ZIP 结构。打开模板用 Word “另存为 → Word 文档(*.docx)” 去掉宏再用。
最麻烦的永远不是填内容,而是模板里那些看不见的样式继承链、隐藏的 SEQ 字段、页眉中的节编号——它们不会报错,但会让最终文档在不同 Word 版本里渲染不一致。动手前先用 Word 打开模板,按 Ctrl+A → Ctrl+Space 清除所有直接格式,再设好样式集。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











