解决 python-docx 生成的 Word 文档打开时弹出“无法读取内容“警告
🤟致敬读者
-
🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉
📘博主相关
-
🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息
文章目录
-
解决 python-docx 生成的 Word 文档打开时弹出”无法读取内容”警告 -
一、问题现象 -
二、排查过程 -
第一步:常规 XML 结构检查 -
第二步:检查 `customXml` 目录 -
第三步:确认完整关系链 -
三、根本原因 -
四、解决方案 -
实现代码 -
调用方式 -
五、方案要点说明 -
为什么不直接修改模板文件? -
ZIP 重打包的注意事项 -
正则表达式中 `[^>]*` 的选择 -
只清理 WPS 私有项,保留其他 customXml -
六、验证方法 -
七、总结
📃文章前言
-
🔷文章均为学习工作中整理的笔记。 -
🔶如有错误请指正,共同学习进步。

解决 python-docx 生成的 Word 文档打开时弹出”无法读取内容”警告
背景:使用 python-docx 基于 WPS 创建的模板生成
.docx报告文件,用 Microsoft Word 打开时弹出以下警告:“Word 在 report_xxx.docx 中发现无法读取的内容。是否恢复此文档的内容?如果您信任此文档的来源,请单击”是”。”
一、问题现象
程序运行正常,生成的 .docx 文件内容完整,但每次用 Microsoft Word 打开都会弹出”无法读取内容”警告弹窗。点击”是”之后文档可以正常显示,但这个警告严重影响用户体验,并暗示文件存在潜在的格式问题。
二、排查过程
第一步:常规 XML 结构检查
首先怀疑是 python-docx 生成的 XML 本身存在格式问题,逐一检查:
word/document.xml
的 body 结构 -
颜色值格式( #000000vs000000) -
图片关系引用 -
样式引用完整性 -
书签配对完整性 -
表格 XML 结构( tblPr/tblGrid/tblW)
结果:全部通过,未发现异常。
插曲:检查过程中确实发现了一个
#000000颜色值格式问题——python-docx 要求颜色值不含#前缀(应为000000),修复后警告依然存在,说明这不是根本原因。
第二步:检查 customXml 目录
将 .docx 文件作为 ZIP 包解压,进入 customXml/ 目录检查:
customXml/├── item1.xml├── item2.xml├── _rels/│ ├── item1.xml.rels│ └── item2.xml.rels├── itemProps1.xml ← 关键文件└── itemProps2.xml
打开 customXml/itemProps1.xml,发现了问题所在:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ds:datastoreItemds:itemID="{B1977F7D-...}"xmlns:ds="..."><ds:schemaRefs><ds:schemaRefds:uri="http://www.wps.cn/officeDocument/2013/wpsCustomData"/></ds:schemaRefs></ds:datastoreItem>
以及 customXml/item1.xml 中的私有数据:
<s:customDataxmlns="http://www.wps.cn/officeDocument/2013/wpsCustomData"><!-- WPS 私有扩展信息:形状、段落属性等 --></s:customData>
第三步:确认完整关系链
word/_rels/document.xml.rels└─ rId12 → ../customXml/item1.xml (WPS 私有数据)└─ rId14 → ../customXml/item2.xml (APA 书目,正常)[Content_Types].xml└─
三、根本原因
模板文件由 WPS(金山 Office)创建,WPS 会在 .docx 文件中嵌入私有的 wpsCustomData XML 数据,其 schema URI 为:
http://www.wps.cn/officeDocument/2013/wpsCustomData
当 python-docx 加载该模板并保存新文档时,这份 WPS 私有 customXml 数据会被完整保留到输出文件中。
Microsoft Word 打开文档时会尝试验证所有 customXml 的 schema 引用。由于 wpsCustomData 是 WPS 的私有 schema,Microsoft Word 无法找到或识别它,因此触发”无法读取内容”警告。
四、解决方案
在 doc.save() 之后,直接对生成的 ZIP 包(即 .docx 文件)进行处理,移除 WPS 私有数据。
实现代码
def clean_wps_custom_data(docx_path):"""从生成的 docx 文件中移除 WPS 私有 customXml 数据。模板由 WPS 创建时会嵌入 wpsCustomData 私有 XML,其 schema URI 为http://www.wps.cn/officeDocument/2013/wpsCustomData,Microsoft Word无法识别此私有 schema,导致打开时弹出"无法读取内容"警告。此函数在 doc.save() 之后运行,直接对 ZIP 包进行手术,移除该私有数据。"""import zipfile, re, iobuffer = io.BytesIO()try:with zipfile.ZipFile(docx_path, 'r') as z_in:with zipfile.ZipFile(buffer, 'w', compression=zipfile.ZIP_DEFLATED) as z_out:file_list = z_in.namelist()# 第一步:找出含有 WPS schema 引用的 itemProps 文件编号wps_item_nums = set()for fname in file_list:if fname.startswith('customXml/itemProps') and fname.endswith('.xml'):with z_in.open(fname) as f:content = f.read().decode('utf-8', errors='replace')if 'wps.cn' in content or 'wpsCustomData' in content:m = re.search(r'itemProps(\d+)\.xml', fname)if m:wps_item_nums.add(m.group(1))# 第二步:构建需要整体删除的文件集合files_to_delete = set()for num in wps_item_nums:files_to_delete.add(f'customXml/item{num}.xml')files_to_delete.add(f'customXml/itemProps{num}.xml')files_to_delete.add(f'customXml/_rels/item{num}.xml.rels')if not wps_item_nums:print('clean_wps_custom_data: 未发现 WPS 私有数据,无需清理')returnprint(f'clean_wps_custom_data: 发现 WPS 私有数据(item 编号 {wps_item_nums}),正在清理...')# 第三步:逐文件处理,重新打包 ZIPfor fname in file_list:if fname in files_to_delete:continue # 跳过 WPS 私有文件,不写入新 zipwith z_in.open(fname) as f:data = f.read()if fname == '[Content_Types].xml':# 删除 WPS itemProps 的 Override 声明content = data.decode('utf-8')for num in wps_item_nums:content = re.sub(r'<Override[^>]*PartName="/customXml/itemProps' + num + r'\.xml"[^>]*/>', '',content)z_out.writestr(fname, content.encode('utf-8'))elif fname == 'word/_rels/document.xml.rels':# 删除指向 WPS item 文件的 Relationship 条目content = data.decode('utf-8')for num in wps_item_nums:content = re.sub(r'<Relationship[^>]*Target="\.\./customXml/item' + num + r'\.xml"[^>]*/>', '',content)z_out.writestr(fname, content.encode('utf-8'))else:z_out.writestr(fname, data)# 将处理后的内容写回原文件buffer.seek(0)with open(docx_path, 'wb') as f:f.write(buffer.read())print('clean_wps_custom_data: WPS 私有数据清理完成')except Exception as e:print(f'clean_wps_custom_data: 清理失败(不影响文档内容): {e}')
调用方式
doc = Document(template_path)# ... 填充内容 ...doc.save(output_path)clean_wps_custom_data(output_path) # ← 紧接在 save 之后调用
五、方案要点说明
为什么不直接修改模板文件?
可以手动用 Microsoft Word 打开模板并重新保存,以去除 WPS 私有数据。但这种方式存在风险:
-
每次模板更新后都需要手动处理 -
在自动化流水线中无法保证
因此选择在代码层面自动清理,更加健壮。
ZIP 重打包的注意事项
.docx 本质上是一个 ZIP 文件。修改其内容的正确方式是:
-
读取原 ZIP 到内存( io.BytesIO) -
创建新 ZIP,逐文件写入(跳过或修改目标文件) -
将新 ZIP 的内容写回原路径
不要直接在原 ZIP 上增删文件(Python 的 zipfile 模块不支持原地删除),否则会损坏文件结构。
正则表达式中 [^>]* 的选择
处理 [Content_Types].xml 和 document.xml.rels 时,需要匹配完整的 XML 元素标签。注意:
# ❌ 错误:[^/]* 会在 ContentType 属性值中的 / 处提前停止r'<Override[^>]*PartName="..."[^/]*/>'# ✅ 正确:[^>]* 只排除 >,允许属性值中包含 /r'<Override[^>]*PartName="..."[^>]*/>'
ContentType="application/vnd.openxmlformats-officedocument.customXmlProperties+xml" 中包含 /,必须使用 [^>]* 才能正确匹配到 />。
只清理 WPS 私有项,保留其他 customXml
文档中可能存在合法的 customXml 数据(如 APA 书目数据),清理时通过检测 wps.cn 域名精确定位,只删除 WPS 私有项,不影响其他数据。
六、验证方法
用 Python 对清理前后的文件进行验证:
import zipfiledef verify_clean(docx_path):with zipfile.ZipFile(docx_path, 'r') as z:file_list = z.namelist()# 1. 检查是否还有 WPS schema 引用for fname in file_list:if fname.startswith('customXml/itemProps') and fname.endswith('.xml'):with z.open(fname) as f:content = f.read().decode('utf-8', errors='replace')if 'wps.cn' in content or 'wpsCustomData' in content:print(f"⚠ 仍有 WPS schema: {fname}")return False# 2. 检查 document.xml.relswith z.open('word/_rels/document.xml.rels') as f:rels = f.read().decode('utf-8')if 'wpsCustomData' in rels or ('customXml' in rels and 'wps.cn' in rels):print("⚠ document.xml.rels 中仍有 WPS 引用")return Falseprint("✓ 清理验证通过,无 WPS 私有数据残留")return True
七、总结
|
|
|
|---|---|
| 问题根因 |
wpsCustomData schema,Microsoft Word 无法识别 |
| 触发条件 |
|
| 修复方式 | doc.save()
|
| 清理范围 | customXml/item{n}.xml
customXml/itemProps{n}.xml、customXml/_rels/item{n}.xml.rels、[Content_Types].xml 中的 Override 声明、word/_rels/document.xml.rels 中的 Relationship 条目 |
| 副作用 |
|
适用场景:所有使用 python-docx 基于 WPS 创建的模板生成 Word 文档的场景,均可能遇到此问题,使用本文的清理方案可彻底解决。

📜文末寄语
-
🟠关注我,解锁更多优质内容 -
🟡技术前沿 | 实战干货 | 疑难解答,持续更新中 -
🟢加入《全栈知识库》,与各领域开发者共创技术盛宴 -
🔵进入《专属社群》,技术路上结伴同行,共同成长 -
🟣点击下方名片,获取更多精彩内容👇
夜雨聆风