docxtpl( python-docx-template)是基于 python-docx 的文档模板引擎,使用非常方便易懂。与 Python 的 Jinja2 模板语法结合使用,通过在 Word 模板文件中设置占位符{{}},并在 Python 里传入一个字典,最终生成一个包含动态数据填充结果的 Word 文档。
1. 安装
pip install docxtpl
2. 使用占位符表示需要替换的内容
(1)第一步:制作word模板
如图所示,将模板中要替换的内容用占位符{{变量名}}来替代,变量名应与python传入的一致。
(2)第二步:编写代码
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm
import datetime
# 加载模板文件
tpl = DocxTemplate('pythondoc/templates/模板.docx')
image1_path = 'pythondoc/pics/bus1.jpg'
image2_path = 'pythondoc/pics/bus2.jpg'
time_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
context = {
'姓名': '张三',
'身高': '180cm',
'年龄': '18',
'性别': '男',
'时间': time_str,
'图片1': InlineImage(tpl, image1_path, width=Mm(40)),
'图片2': InlineImage(tpl, image2_path, width=Mm(40))
}
tpl.render(context)
# 保存生成的文档
tpl.save('pythondoc/templates/output.docx')
print("替换完成,已生成 output.docx")
(3)第三步:查看生成结果
3. 模板中包含循环内容,如表格
如以上体检报告模板中,在末尾添加一个表格用来记录各项体检结果
(1) 第一步 修改模板:在模板中增加表格
请注意:
在循环开始的地方加{%for result in results%},results应该与python中传入的变量名一致;
在循环结束的地方加{%endfor%}
(2) 第二步:编写代码
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm
import datetime
# 加载模板文件
tpl = DocxTemplate('pythondoc/templates/模板.docx')
image1_path = 'pythondoc/pics/bus1.jpg'
image2_path = 'pythondoc/pics/bus2.jpg'
time_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
context = {
'姓名': '张三',
'身高': '180cm',
'年龄': '18',
'性别': '男',
'时间': time_str,
'图片1': InlineImage(tpl, image1_path, width=Mm(40)),
'图片2': InlineImage(tpl, image2_path, width=Mm(40)),
'results': [
{'检查项':'检查项1', '检查结果':'正常', '参考值': '80-100'},
{'检查项':'检查项2', '检查结果':'正常', '参考值': '45-55'},
{'检查项':'检查项3', '检查结果':'异常', '参考值': '45-55'}
]
}
tpl.render(context)
# 保存生成的文档
tpl.save('pythondoc/templates/output.docx')
print("替换完成,已生成 output.docx")
(3) 第三步:查看生成结果
4. 多个模板内容合并成一个文档
(1) 第一步:修改模板
其实,使用起来跟生成表格是一样的,只是标记开始的位置不一样。
使用for循环标记要开始重复生成的位置,如下图。想从哪开始重复就将{% for result in results %}写在哪,这个占位符标记着开始循环的位置。
在结束的位置写上{%endfor%},标记循环结束的地方。因为我希望更清晰美观一点,于是在for循环结束之前加了一行空白内容。
(2)第二步:编写代码
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Mm
import datetime
# 加载模板文件
tpl = DocxTemplate('pythondoc/templates/模板v2.docx')
image1_path = 'pythondoc/pics/bus1.jpg'
image2_path = 'pythondoc/pics/bus2.jpg'
time_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
context = {
'results':[
{
'姓名': '张三',
'身高': '180cm',
'年龄': '18',
'性别': '男',
'时间': time_str,
'图片1': InlineImage(tpl, image1_path, width=Mm(40)),
'图片2': InlineImage(tpl, image2_path, width=Mm(40))
},
{
'姓名': '李四',
'身高': '160cm',
'年龄': '18',
'性别': '女',
'时间': time_str,
'图片1': InlineImage(tpl, image1_path, width=Mm(40)),
'图片2': InlineImage(tpl, image2_path, width=Mm(40))
}
]
}
tpl.render(context)
# 保存生成的文档
tpl.save('pythondoc/templates/output.docx')
print("替换完成,已生成 output.docx")
(3) 第三步:查看生成结果
5. 格式问题
生成内容的格式与占位符的格式是一致的,所以只要调整好占位符的格式就行。
6. 一次错误的尝试
最开始没有在模板中使用for循环,想着用docx将产生的新模板的内容全部拼接到产生的第一个文档或某个固定文档中,代码如下
main_doc = Document('pythondoc/templates/模板v2.docx')
tem_doc = tpl.docx
for element in tem_doc.element.body:
main_doc.element.body.append(element)
最后发现,使用该方式,文字内容全都拼接过去了,甚至表格也过去了,但是图片一直都是第一次生成的图片,也就是说明明是两张不一样的图片,InlineImage(tpl, image1_path, width=Mm(40))中设置的路径也不一样,但是图片都是第一张,问AI说是docx对于图片有缓存机制。
总之,docxtpl太香啦,代码很简单,可以很方便地替换模板内容,生成循环内容,还不用费劲去调格式。