Python docx批量生成Word时循环替换内容不更新问题
问题描述
通过嵌套for循环遍历ID值列表,依托python-docx模块替换Word模板中的指定占位符,预期每次循环更新一个ID值,将生成的新文档以当前ID值命名保存。代码运行存在异常:输出的Word文件名可随循环正常迭代更新,但所有文档内的$idnum占位符始终被替换为ident列表的第一个值(例如列表首值为"B25"时,所有文档对应位置均显示B25),替换值不会随当前循环的cn变量更新。
原始实现代码:
def makeWord(ident): #'ident' is a list of alphanumeric I.D. numbers wordpath = path + 'Word_Template.docx' outpath = path + 'Word Files\\' wordfile = Document(wordpath) for cn in ident: myvars = {'$MO':datamonth, '$YEAR':datayear, '$idnum', cn} #data month/year are constants for key, val in myvars.items(): for para in wordfile.paragraphs: replace_text_in_paragraph(para, key, val) saveword(wordfile, cn, outpath) def replace_text_in_paragraph(paragraph, key, value): if key in paragraph.text: inline = paragraph.runs for item in inline: if key in item.text: item.text = item.text.replace(key, value) def saveword(wordfile, idnum, outp): wordfile.save(outp + idnum + '_' + datamonth + '_' + datayear + '.docx')
问题根因
- 模板加载逻辑错误:仅在for循环外加载了一次Word模板对象,第一次循环执行时就已经把模板里的
$idnum占位符替换成了列表第一个ID值,后续循环操作的是已经被修改、不存在$idnum占位符的文档对象,替换逻辑无法触发,因此只有文件名随循环变量更新。 - 字典语法错误:
myvars定义中'$idnum', cn使用逗号分隔,不符合字典键值对用冒号分隔的语法规则,会被解析为集合类型,调用.items()方法时会直接抛出异常。 - 替换逻辑兼容缺陷:python-docx会按文本格式自动将段落拆分为多个独立run,占位符可能被拆分到不同run中,原有逐run匹配的逻辑遇到这类情况会替换失败。
修正代码
每次循环重新加载原始未修改的模板,同时修正字典语法、补全跨run替换的兼容逻辑:
def makeWord(ident): #'ident' is a list of alphanumeric I.D. numbers wordpath = path + 'Word_Template.docx' outpath = path + 'Word Files\\' for cn in ident: # 每次循环重新读取原始模板,不复用已修改的文档对象 wordfile = Document(wordpath) # 修正字典键值对语法 myvars = {'$MO':datamonth, '$YEAR':datayear, '$idnum': cn} for key, val in myvars.items(): for para in wordfile.paragraphs: replace_text_in_paragraph(para, key, str(val)) saveword(wordfile, cn, outpath) def replace_text_in_paragraph(paragraph, key, value): # 段落不含目标key直接跳过 if key not in paragraph.text: return # 拼接所有run文本,解决占位符被拆分到多个run的问题 full_text = ''.join([run.text for run in paragraph.runs]) if key in full_text: replaced_text = full_text.replace(key, value) # 替换后的文本写入第一个run,其余run清空 paragraph.runs[0].text = replaced_text for run in paragraph.runs[1:]: run.text = '' def saveword(wordfile, idnum, outp): wordfile.save(f"{outp}{idnum}_{datamonth}_{datayear}.docx")
内容的提问来源于stack exchange,提问作者DirtyHarree
相关产品推荐
相关产品推荐

