Python使用python-docx-template的實(shí)用案例解析
Python-docx-template是一個(gè)功能強(qiáng)大的Word文檔自動(dòng)化生成庫(kù),它基于模板引擎的設(shè)計(jì)思想,允許用戶通過(guò)編寫模板與Python代碼邏輯分離的方式,高效生成結(jié)構(gòu)復(fù)雜、樣式多樣的Word文檔。在文章python-docx-template模板化Word文檔生成指北介紹該庫(kù)的基礎(chǔ)用法之上,本文將進(jìn)一步結(jié)合官方示例,提供多個(gè)實(shí)用場(chǎng)景的代碼解析與拓展,涵蓋復(fù)雜樣式、自定義過(guò)濾器、嵌套循環(huán)及富文本渲染等高級(jí)功能,以提升文檔生成的效率與靈活性。下文將分模塊展開(kāi)具體示例與實(shí)現(xiàn)。
python-docx-template的官方代碼倉(cāng)庫(kù)地址為:python-docx-template,詳細(xì)文檔可參閱:python-docx-template doc。

本文使用的python-docx-template版本為0.20.2,安裝命令如下:
pip install docxtpl
表格樣式生成
本示例用于生成包含富文本樣式與單元格背景色的Word表格文檔。
模板內(nèi)容:

渲染代碼:
# python-docx-template/blob/master/tests/comments.py
from docxtpl import DocxTemplate, RichText
# data: python-docx-template/blob/master/tests/templates/cellbg_tpl.docx
tpl = DocxTemplate("templates/cellbg_tpl.docx")
context = {
"alerts": [
{
"date": "2015-03-10",
"desc": RichText("Very critical alert", color="FF0000", bold=True),
"type": "CRITICAL",
"bg": "FF0000",
},
{
"date": "2015-03-11",
"desc": RichText("Just a warning"),
"type": "WARNING",
"bg": "FFDD00",
}
],
}
tpl.render(context)
tpl.save("output/cellbg.docx")
自定義Jinja2過(guò)濾器
本示例主要介紹通過(guò)自定義Jinja2過(guò)濾器實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)渲染,Jinja2模板中過(guò)濾器的核心格式為:{{ 變量名|過(guò)濾器名(參數(shù)1, 參數(shù)2, ...) }},其中:
|(豎線)是過(guò)濾器的分隔符,左側(cè)是要處理的變量,右側(cè)是過(guò)濾器名稱;- 括號(hào)
()內(nèi)是傳給過(guò)濾器函數(shù)的參數(shù)(無(wú)參數(shù)時(shí)可省略括號(hào)); - 示例:
{{ base_value_float|my_filterB(2) }}中,base_value_float是變量,my_filterB是過(guò)濾器名,2是傳遞的參數(shù)。
模板內(nèi)容:

渲染代碼:
# python-docx-template/blob/master/tests/custom_jinja_filters.py
from docxtpl import DocxTemplate
import jinja2
# 創(chuàng)建jinja2環(huán)境對(duì)象,用于管理模板渲染的配置
jinja_env = jinja2.Environment()
# 自定義過(guò)濾器函數(shù)
def my_filterA(value, my_string_arg):
# 將原始值和參數(shù)字符串拼接,中間加空格
return_value = value + " " + my_string_arg
return return_value
def my_filterB(value, my_float_arg):
# 將原始值和參數(shù)數(shù)值相加
return_value = value + my_float_arg
return return_value
# 將自定義過(guò)濾器注冊(cè)到j(luò)inja2環(huán)境中,使其能在模板中被調(diào)用
# 注冊(cè)后在Word模板中可通過(guò){{ 變量名| my_filterA('參數(shù)') }}形式使用
jinja_env.filters["my_filterA"] = my_filterA
jinja_env.filters["my_filterB"] = my_filterB
context = {
"base_value_string": " Hello",
"base_value_float": 1.5
}
# data: python-docx-template/blob/master/tests/templates/custom_jinja_filters_tpl.docx
tpl = DocxTemplate("templates/custom_jinja_filters_tpl.docx")
tpl.render(context, jinja_env)
tpl.save("output/custom_jinja_filters.docx")
文檔嵌入
以下代碼展示了如何渲染子Word文檔,替換主Word文檔中嵌入的各類文件,填充數(shù)據(jù)后保存文檔:
# python-docx-template/blob/master/tests/embedded.py
from docxtpl import DocxTemplate
# 加載內(nèi)嵌子模板文件
# data: python-docx-template/blob/master/tests/templates/embedded_embedded_docx_tpl.docx
embedded_docx_tpl = DocxTemplate("templates/embedded_embedded_docx_tpl.docx")
# 定義模板渲染的上下文數(shù)據(jù)
context = {
"name": "John Doe", # 要填充到模板中的姓名值
}
embedded_docx_tpl.render(context)
# 保存渲染后的子模板到指定路徑,供后續(xù)主模板調(diào)用
embedded_docx_tpl.save("output/embedded_embedded_docx.docx")
# 加載主模板文件
tpl = DocxTemplate("templates/embedded_main_tpl.docx")
# 定義主模板的上下文數(shù)據(jù)
context = {
"name": "John Doe",
}
# 替換主模板中嵌入的Word文檔
# 參數(shù)1:模原本嵌入的占位文件路徑
# 參數(shù)2:要替換成的目標(biāo)文件路徑
tpl.replace_embedded(
"templates/embedded_dummy.docx", "templates/embedded_static_docx.docx"
)
tpl.replace_embedded(
"templates/embedded_dummy2.docx", "output/embedded_embedded_docx.docx"
)
# 說(shuō)明:docx 本質(zhì)是 zip 壓縮包,嵌入的文件會(huì)存儲(chǔ)在word/embeddings/目錄下
tpl.replace_zipname(
"word/embeddings/Feuille_Microsoft_Office_Excel3.xlsx",
"templates/real_Excel.xlsx" # 要替換成的實(shí)際文件路徑
)
tpl.replace_zipname(
"word/embeddings/Pr_sentation_Microsoft_Office_PowerPoint4.pptx",
"templates/real_PowerPoint.pptx"
)
tpl.render(context)
tpl.save("output/embedded.docx")
自動(dòng)轉(zhuǎn)義
本示例展示了在自動(dòng)轉(zhuǎn)義模式下,將包含XML特殊字符、Unicode文本和動(dòng)態(tài)鍵值對(duì)的上下文數(shù)據(jù)渲染到模板中。
模板內(nèi)容:

渲染代碼:
# python-docx-template/blob/master/tests/escape_auto.py
import os
from unicodedata import name
from docxtpl import DocxTemplate
XML_RESERVED = """<"&'>"""
# data: python-docx-template/blob/master/tests/templates/escape_tpl_auto.docx
tpl = DocxTemplate("templates/escape_tpl_auto.docx")
context = {
"nested_dict": {name(str(c)): c for c in XML_RESERVED},
"autoescape": 'Escaped "str & ing"!',
"autoescape_unicode": "This is an escaped <unicode> example \u4f60 & \u6211",
"iteritems": lambda x: x.items(),
}
# autoescape=True表示自動(dòng)轉(zhuǎn)義
tpl.render(context, autoescape=True)
OUTPUT = "output"
if not os.path.exists(OUTPUT):
os.makedirs(OUTPUT)
tpl.save(OUTPUT + "/escape_auto.docx")
實(shí)際上iteritems(nested_dict)就是調(diào)用渲染定義的lambda函數(shù),把nested_dict傳進(jìn)去,拿到它的所有鍵值對(duì):
{% for k, v in iteritems(nested_dict) %}
{{ k.capitalize() }}: {{ v }}{% endfor %}
也可以在Jinja2模板中使用Python表達(dá)式,直接調(diào)用字典的item方法:
{% for k, v in nested_dict.items() %}
{{ k.capitalize() }}: {{ v }}{% endfor %}
圖片替換
以下示例說(shuō)明如何替換Word模板文檔(包含頁(yè)眉頁(yè)腳)中的圖片,并演示如何將處理后的文檔分別通過(guò)常規(guī)方式和內(nèi)存文件對(duì)象保存為本地文件:
# python-docx-template/blob/master/tests/header_footer_image_file_obj.py
from docxtpl import DocxTemplate
import io
# 定義兩個(gè)輸出文檔的路徑和文件名
DEST_FILE = "output/header_footer_image_file_obj.docx"
DEST_FILE2 = "output/header_footer_image_file_obj2.docx"
# data: python-docx-template/blob/master/tests/templates/header_footer_image_tpl.docx
tpl = DocxTemplate("templates/header_footer_image_tpl.docx")
context = {
"mycompany": "The World Wide company",
}
# 讀取模板中需要被替換的圖片文件,并轉(zhuǎn)換為內(nèi)存字節(jié)流對(duì)象
dummy_pic = io.BytesIO(open("templates/dummy_pic_for_header.png", "rb").read())
# 讀取新的替換圖片文件(python.png),并轉(zhuǎn)換為內(nèi)存字節(jié)流對(duì)象
new_image = io.BytesIO(open("templates/python.png", "rb").read())
# 將dummy_pic對(duì)應(yīng)的圖片替換為new_image對(duì)應(yīng)的圖片
tpl.replace_media(dummy_pic, new_image)
tpl.render(context)
tpl.save(DEST_FILE)
tpl = DocxTemplate("templates/header_footer_image_tpl.docx")
# 將內(nèi)存中的圖片字節(jié)流指針重置到起始位置
dummy_pic.seek(0)
new_image.seek(0)
# 再次執(zhí)行圖片替換操作
tpl.replace_media(dummy_pic, new_image)
# 再次渲染模板變量
tpl.render(context)
# 創(chuàng)建一個(gè)空的內(nèi)存字節(jié)流對(duì)象,用于臨時(shí)存儲(chǔ)文檔內(nèi)容
file_obj = io.BytesIO()
# 將處理后的文檔保存到內(nèi)存字節(jié)流對(duì)象中
tpl.save(file_obj)
# 將內(nèi)存字節(jié)流指針重置到起始位置,準(zhǔn)備讀取內(nèi)容
file_obj.seek(0)
# 以二進(jìn)制寫入模式打開(kāi)第二個(gè)輸出文件,將內(nèi)存中的文檔內(nèi)容寫入文件
with open(DEST_FILE2, "wb") as f:
f.write(file_obj.read())
dummy_pic.close()
new_image.close()
上述代碼實(shí)現(xiàn)圖片替換并非基于文件名,而是基于二進(jìn)制內(nèi)容的匹配。這是因?yàn)?code>replace_media方法根據(jù)圖片的二進(jìn)制內(nèi)容來(lái)識(shí)別圖像,而非依賴文件名或在Word中顯示的名稱。
由于Word文檔 (.docx) 本質(zhì)上是一個(gè)壓縮包,其中的圖片以二進(jìn)制形式存儲(chǔ)在word/media/目錄下,且在某些Word版本中,圖片文件名可能被自動(dòng)重命名(例如改為 image1.png),與原始文件名無(wú)關(guān)。
注意:待替換圖片尺寸不宜過(guò)大,且需關(guān)閉Word模板的圖片壓縮功能;否則Word會(huì)自動(dòng)壓縮模板中的圖片,改變其二進(jìn)制數(shù)據(jù),最終導(dǎo)致圖片替換操作失敗。
命令行執(zhí)行
以下示例展示了直接在命令行中使用docxtpl模塊,基于模板文件和作為上下文數(shù)據(jù)的JSON文件生成docx文檔:
# python-docx-template/blob/master/tests/module_execute.py
import os
# data: python-docx-template/blob/master/tests/templates/module_execute_tpl.docx
TEMPLATE_PATH = "templates/module_execute_tpl.docx"
# 存儲(chǔ)需要填充到模板中的數(shù)據(jù)
JSON_PATH = "templates/module_execute.json"
OUTPUT_FILENAME = "output/module_execute.docx"
# docxtpl命令參數(shù):強(qiáng)制覆蓋已存在的輸出文件
OVERWRITE = "-o"
# docxtpl命令參數(shù):靜默模式執(zhí)行,不輸出額外日志信息
QUIET = "-q"
# 刪除已存在的輸出文件
if os.path.exists(OUTPUT_FILENAME):
os.unlink(OUTPUT_FILENAME)
# 切換工作目錄到當(dāng)前腳本所在的目錄
os.chdir(os.path.dirname(__file__))
# 通過(guò)Python模塊方式調(diào)用docxtpl,傳入模板、數(shù)據(jù)、輸出路徑和參數(shù)
# 可通過(guò)python -m docxtpl -help查看調(diào)用幫助
cmd = "python -m docxtpl %s %s %s %s %s" % (
TEMPLATE_PATH, # 模板文件路徑
JSON_PATH, # 數(shù)據(jù)文件路徑
OUTPUT_FILENAME, # 輸出文件路徑
OVERWRITE, # 覆蓋參數(shù)
QUIET # 靜默參數(shù)
)
print('Executing "%s" ...' % cmd)
os.system(cmd)
if os.path.exists(OUTPUT_FILENAME):
print(" --> File %s has been generated." % OUTPUT_FILENAME)
多層嵌套
以下示例展示了如何通過(guò)模板語(yǔ)法實(shí)現(xiàn)逐層循環(huán)渲染,最終生成包含這些嵌套數(shù)據(jù)的Word文檔:
# python-docx-template/blob/master/tests/nested_for.py
from docxtpl import DocxTemplate
# data: python-docx-template/blob/master/tests/templates/nested_for_tpl.docx
tpl = DocxTemplate("templates/nested_for_tpl.docx")
context = {
"dishes": [
{"name": "Pizza", "ingredients": ["bread", "tomato", "ham"]},
{
"name": "Hamburger",
"ingredients": ["bread", "chopped steak", "cheese"],
},
],
"authors": [
{
"name": "Saint-Exupery",
"books": [
{"title": "Le petit prince"},
{"title": "L'aviateur"},
],
},
{
"name": "Barjavel",
"books": [
{"title": "Ravage"},
{"title": "La nuit des temps"},
],
},
],
}
tpl.render(context)
tpl.save("output/nested_for.docx")
地區(qū)字體處理
若字體顯示異常,通常是由于字體僅適配了特定文字區(qū)域。解決方法是在字體名前加上區(qū)域標(biāo)識(shí)和冒號(hào)(如 eastAsia:微軟雅黑),從而指定文字的區(qū)域渲染方式。如果不清楚區(qū)域標(biāo)識(shí),也可解壓模板文件后分析document.xml確認(rèn)字體對(duì)應(yīng)的區(qū)域。常見(jiàn)區(qū)域標(biāo)識(shí)包括:
- eastAsia:用于東亞字符如中文
- hAnsi:用于拉丁字符如英文
- ascii:用于兼容舊版英文
以下代碼展示了如何設(shè)置不同的東亞字體:
# python-docx-template/blob/master/tests/richtext_eastAsia.py
from docxtpl import DocxTemplate, RichText
# data: python-docx-template/blob/master/tests/templates/richtext_eastAsia_tpl.docx
tpl = DocxTemplate("templates/richtext_eastAsia_tpl.docx")
# 2. 創(chuàng)建富文本對(duì)象,分別設(shè)置不同的東亞字體
# eastAsia: 前綴表示該字體設(shè)置僅作用于東亞字符(中文、日文、韓文等)
rt = RichText("測(cè)試TEST", font="eastAsia:Microsoft YaHei")
ch = RichText("測(cè)試TEST", font="eastAsia:微軟雅黑")
sun = RichText("測(cè)試TEST", font="eastAsia:SimSun")
context = {
"example": rt,
"Chinese": ch,
"simsun": sun,
}
tpl.render(context)
tpl.save("output/richtext_eastAsia.docx")
富文本使用
Python-docx-template的核心功能是基于Jinja2語(yǔ)法動(dòng)態(tài)生成Word文檔。其RichText類進(jìn)一步增強(qiáng)了靈活性,允許直接以編程方式插入格式豐富的文本,而無(wú)需為每種樣式組合單獨(dú)設(shè)置模板變量。RichText對(duì)象可在初始化時(shí)直接傳入文本,通過(guò)多次調(diào)用add方法可向其追加不同格式的文本片段。最后,將該對(duì)象整體賦給模板上下文中的變量。在Word模板中,只需使用{{ rich_text_var }}引用該變量,即可渲染成包含多種格式的連續(xù)段落。

add方法是構(gòu)建RichText對(duì)象的核心,其所有參數(shù)均用于控制當(dāng)前操作所添加文本的格式:
| 參數(shù) | 類型 | 默認(rèn)值 | 說(shuō)明 |
|---|---|---|---|
| text | str | 無(wú) | 唯一必需參數(shù),要追加的文本內(nèi)容 |
| style | str | None | 應(yīng)用段落樣式,這會(huì)影響整個(gè)由RichText對(duì)象生成的段落的樣式 |
| color | str | None | 字體顏色支持十六進(jìn)制(如#FF0000)或Word預(yù)設(shè)顏色名(如red) |
| highlight | str | None | 文本背景高亮色,取值同color參數(shù) |
| size | int | None | 字體大小 |
| subscript | bool | None | 設(shè)為True時(shí)文本顯示為下標(biāo),與superscript互斥 |
| superscript | bool | None | 設(shè)為True時(shí)文本顯示為上標(biāo),與subscript互斥 |
| bold | bool | False | 設(shè)為True時(shí),文本加粗 |
| italic | bool | False | 設(shè)為True時(shí),文本傾斜 |
| underline | bool | False | 設(shè)為True時(shí),文本添加下劃線 |
| strike | bool | False | 設(shè)為True時(shí),文本添加刪除線 |
| font | str | None | 字體名稱 |
| url_id | str | None | 添加超鏈接,需要傳入一個(gè)鏈接ID,該ID一般通過(guò)文檔對(duì)象的build_url_id()方法生成 |
| rtl | bool | False | 設(shè)為True時(shí),文本從右向左排列,僅對(duì)阿拉伯語(yǔ)、希伯來(lái)語(yǔ)等有效 |
| lang | str | None | 設(shè)置文本的語(yǔ)言,用于拼寫檢查和斷字 |
重要提示:style(段落樣式)參數(shù)比較特殊。它通常只在第一次調(diào)用add方法時(shí)有效,后續(xù)調(diào)用中再設(shè)置style通常會(huì)被忽略。
以下是一段使用示例代碼,按順序介紹RichText.add()方法的各個(gè)參數(shù):
from docxtpl import DocxTemplate, RichText
import os
# 1. 創(chuàng)建模板對(duì)象并初始化RichText
doc = DocxTemplate("template.docx")
rt = RichText()
# 2. 添加基礎(chǔ)文本(必需參數(shù))
rt.add("這是普通文本")
# 3. 設(shè)置段落樣式(影響整個(gè)段落)
rt.add("\n標(biāo)題文本", style="Heading1")
# 4. 設(shè)置字體顏色
rt.add(" 紅色文字", color="#FF0000")
rt.add(" 藍(lán)色文字", color="blue")
# 5. 設(shè)置背景高亮
rt.add(" 黃底文字", highlight="yellow")
# 6. 設(shè)置字體大小(單位:磅)
rt.add(" 小號(hào)字", size=8)
rt.add(" 大號(hào)字", size=20)
# 7. 上下標(biāo)設(shè)置
rt.add(" 正常文字")
rt.add(" 上標(biāo)", superscript=True)
rt.add(" 下標(biāo)", subscript=True)
# 8. 字體樣式
rt.add(" 加粗", bold=True)
rt.add(" 傾斜", italic=True)
rt.add(" 下劃線", underline=True)
rt.add(" 刪除線", strike=True)
# 9. 字體設(shè)置
rt.add(" 宋體", font="eastAsia:SimSun")
rt.add(" 微軟雅黑", font="eastAsia:Microsoft YaHei")
# 10. 超鏈接(需要先生成鏈接ID)
url_id = doc.build_url_id("https://www.example.com")
rt.add(" 超鏈接文本", url_id=url_id)
# 11. 文字方向
rt.add(" 正常方向")
rt.add(" 從右向左文字", rtl=True) # 中文設(shè)置無(wú)效果
# 12. 語(yǔ)言設(shè)置
rt.add(" English text", lang="en-US")
rt.add(" 中文文本", lang="zh-CN")
context = {
'rich_text_var': rt
}
doc.render(context)
os.makedirs('output',exist_ok=True)
doc.save("output/generated_document.docx")
在模板中只需簡(jiǎn)單引用:
{{ rich_text_var }}
錯(cuò)誤管理
TemplateError類是Jinja2模板引擎中所有模板相關(guān)異常的基類,在python-docx-template中專門用于捕獲模板渲染過(guò)程中出現(xiàn)的各類錯(cuò)誤。當(dāng)使用tpl.render()渲染 Word 模板時(shí),以下情況會(huì)拋出TemplateError異常:
- 模板中引用了未傳入的變量(如模板寫了
{{ name }},但render只傳了test_variable); - 模板中的Jinja2語(yǔ)法錯(cuò)誤(如缺少閉合的
{% endif %}、變量引用格式錯(cuò)誤); - 模板中使用了不存在的過(guò)濾器/函數(shù)(如
{{ test_variable | xxx }},xxx不是 Jinja2 內(nèi)置過(guò)濾器)。
以下代碼展示了如何測(cè)捕獲Word模板渲染時(shí)的TemplateError異常,并打印詳細(xì)的錯(cuò)誤信息:
from docxtpl import DocxTemplate
from jinja2.exceptions import TemplateError
print("=" * 50)
print("正在生成測(cè)試用的模板錯(cuò)誤")
print("." * 50)
try:
tpl = DocxTemplate("templates/template_error_tpl.docx")
# 如果模板中存在語(yǔ)法錯(cuò)誤或變量缺失,會(huì)觸發(fā)TemplateError異常
tpl.render({"test_variable": "測(cè)試變量值"})
# 捕獲模板渲染過(guò)程中出現(xiàn)的所有TemplateError異常
except TemplateError as the_error:
# 打印錯(cuò)誤的基本描述信息
print(f"模板渲染錯(cuò)誤:{str(the_error)}")
# 檢查異常對(duì)象是否包含docx_context屬性
if hasattr(the_error, "docx_context"):
# 打印上下文信息的標(biāo)題
print("錯(cuò)誤上下文詳情:")
# 遍歷并打印錯(cuò)誤上下文的每一行內(nèi)容
for line in the_error.docx_context:
print(line)
# 確保tpl變量存在時(shí)再執(zhí)行保存操作
if 'tpl' in locals():
# 將渲染后的文檔保存到指定路徑
tpl.save("output/template_error.docx")
print(f"文檔已保存至:output/template_error.docx")
print("." * 50)
print(" 模板錯(cuò)誤測(cè)試完成 ")
print("=" * 50)
到此這篇關(guān)于Python使用python-docx-template的實(shí)用案例解析的文章就介紹到這了,更多相關(guān)Python python-docx-template用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
從正則到?BERT詳解Python如何判斷文本是否為標(biāo)題
在做文檔解析(PDF/Word)或者清洗用戶生成內(nèi)容(UGC)時(shí),我們經(jīng)常面臨一個(gè)尷尬的問(wèn)題,怎么知道哪句是標(biāo)題,哪句是正文,本文將從規(guī)則匹配到深度學(xué)習(xí),層層遞進(jìn),帶你搞定這個(gè)問(wèn)題2026-04-04
解決pycharm上的jupyter notebook端口被占用問(wèn)題
今天小編就為大家分享一篇解決pycharm上的jupyter notebook端口被占用問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-12-12

