当用户需要基于模板填充 Word 文档(.docx)、从模板生成报告、创建包含动态数据的合同,或自动化文档生成时使用此技能。包括替换普通占位符 {name} 替换文本、使用 {name|r:x,c:y} 格式标记的智能表格填充(支持从标记行开始向下填充,保留上方内容)、插入图片、批量生成文档等。如果用户提及 .docx 模板、邮件合并功能或以编程方式填写 Word 表单,请使用此技能。
本指南介绍如何使用 Python 向 Word(.docx)模板填充动态数据。支持:
表格并非总是提前自动扩展 - 这取决于占位符是否被正确识别:
python
from docx import Document
from docx.shared import Inches
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
import re
import os
class DocxTemplateFiller:
def init(self, template_path):
if not os.path.exists(template_path):
raise FileNotFoundError(f模板文件不存在: {template_path})
self.doc = Document(template_path)
self.templatepath = templatepath
self.named_tables = {} # 存储表格信息
# 初始化时扫描表格占位符
self.processtable_placeholders()
def processtable_placeholders(self):
扫描所有表格的每一行第一个单元格,查找 {name|r:x,c:y} 格式
例如:{products|r:5,c:4} 表示从当前行开始,共5行,4列
pattern = re.compile(r\{(\w+)\|r:(\d+),c:(\d+)\})
for table_idx, table in enumerate(self.doc.tables):
for row_idx, row in enumerate(table.rows):
if len(row.cells) == 0:
continue
# 检查每行的第一个单元格(最左侧)
first_cell = row.cells[0]
text = first_cell.text.strip()
match = pattern.search(text)
if match:
name = match.group(1)
target_rows = int(match.group(2))
target_cols = int(match.group(3))
# 保存表格配置信息
self.named_tables[name] = {
table: table,
startrow: rowidx, # 占位符所在行(从此行开始填充)
targetrows: targetrows, # 需要填充的总行数(包括占位符行)
targetcols: targetcols # 目标列数
}
# 调整表格大小:上方保留 rowidx 行,从 rowidx 开始有 target_rows 行
self.resizetable(table, rowidx, targetrows, target_cols)
# 清除占位符文本(可选,因为填充时会覆盖,但清除更干净)
# 保留单元格其他可能的文本(占位符前后文本)
newtext = pattern.sub(, firstcell.text).strip()
if new_text:
firstcell.text = newtext
else:
first_cell.text = # 清空等待填充
def resizetable(self, table, startrow, targetrows, target_cols):
调整表格大小:确保从 startrow 开始有 targetrows 行,总列数为 target_cols
totalneededrows = startrow + targetrows
current_rows = len(table.rows)
current_cols = len(table.columns) if table.columns else 0
# 调整行数
if totalneededrows > current_rows:
# 添加行
for in range(totalneededrows - currentrows):
table.add_row()
elif totalneededrows < current_rows:
# 删除多余行(从末尾删除,保留前面的)
self.deleterowsfromend(table, currentrows - totalneeded_rows)
# 调整列数
if targetcols != currentcols:
self.resizecolumns(table, target_cols)
def deleterowsfromend(self, table, num_rows):
从表格末尾删除指定行数
tbl = table._tbl
for in range(numrows):
if len(table.rows) > 0:
tr = table.rows[-1]._tr
tbl.remove(tr)
def resizecolumns(self, table, target_cols):
调整表格列数
current_cols = len(table.columns)
tbl = table._tbl
tblGrid = tbl.find(qn(w:tblGrid))
if targetcols > currentcols:
# 添加列定义
for in range(targetcols - current_cols):
gridCol = OxmlElement(w:gridCol)
tblGrid.append(gridCol)
# 为每一行添加单元格
for row in table.rows:
for in range(targetcols - current_cols):
tc = OxmlElement(w:tc)
tcPr = OxmlElement(w:tcPr)
tc.append(tcPr)
p = OxmlElement(w:p)
tc.append(p)
row._tr.append(tc)
elif targetcols < currentcols:
# 删除多余列定义
for in range(currentcols - target_cols):
if len(tblGrid) > 0:
tblGrid.remove(tblGrid[-1])
# 从每行删除多余单元格
for row in table.rows:
for in range(currentcols - target_cols):
tcs = row._tr.findall(qn(w:tc))
if len(tcs) > target_cols:
row._tr.remove(tcs[-1])
def fillplaceholders(self, datadict):
替换普通占位符 {key}(不包括表格定义格式)
# 匹配普通占位符,排除表格定义格式
pattern = re.compile(r\{(\w+)\}(?!\|r:\d+,c:\d+))
# 处理段落
for para in self.doc.paragraphs:
self.replaceinparagraph(para, pattern, datadict)
# 处理表格内的普通占位符(排除已识别的表格占位符单元格)
for table in self.doc.tables:
for row in table.rows:
for cell in row.cells:
for para in cell.paragraphs:
self.replaceinparagraph(para, pattern, datadict)
def replaceinparagraph(self, paragraph, pattern, datadict):
在段落中执行替换
text = paragraph.text
matches = pattern.findall(text)
if not matches:
return
new_text = text
for key in matches:
if key in data_dict:
placeholder = f{{{key}}}
value = str(data_dict[key])
newtext = newtext.replace(placeholder, value)
if new_text != text and paragraph.runs:
paragraph.runs[0].text = new_text
for run in paragraph.runs[1:]:
run.text =
def fillnamedtable(self, table_name, data):
填充指定名称的表格
从占位符所在行开始(包括该行)向下填充,保留上方内容
:param table_name: 表格名称(来自占位符 {name|r:x,c:y})
:param data: 二维列表,如 [[产品A, 规格1, 10], [...]]
if tablename not in self.namedtables:
raise KeyError(f未找到名为 {tablename} 的表格,请确保模板中存在 {{{tablename}|r:x,c:y}} 格式的占位符在最左侧单元格)
tableinfo = self.namedtables[table_name]
table = table_info[table]
startrow = tableinfo[start_row]
targetrows = tableinfo[target_rows]
targetcols = tableinfo[target_cols]
# 确保数据不超过声明的行数
if len(data) > target_rows:
data = data[:target_rows]
# 从 start_row 开始填充(包括该行)
for rowoffset, rowdata in enumerate(data):
actualrowidx
该技能支持在以下平台通过对话安装:
帮我安装 SkillHub 和 fill-docx-template-1776072988 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 fill-docx-template-1776072988 技能
skillhub install fill-docx-template-1776072988
文件大小: 7.59 KB | 发布时间: 2026-4-15 12:44