🤟致敬读者
- 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉
📘博主相关
文章目录
- [解决 python-docx 生成的 Word 文档打开时弹出"无法读取内容"警告](#解决 python-docx 生成的 Word 文档打开时弹出"无法读取内容"警告)
📃文章前言
- 🔷文章均为学习工作中整理的笔记。
- 🔶如有错误请指正,共同学习进步。

解决 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
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ds:datastoreItem ds:itemID="{B1977F7D-...}" xmlns:ds="...">
<ds:schemaRefs>
<ds:schemaRef ds:uri="http://www.wps.cn/officeDocument/2013/wpsCustomData"/>
</ds:schemaRefs>
</ds:datastoreItem>
以及 customXml/item1.xml 中的私有数据:
xml
<s:customData xmlns="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
└─ <Override PartName="/customXml/itemProps1.xml" ContentType="...customXmlProperties+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 私有数据。
实现代码
python
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, io
buffer = 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 私有数据,无需清理')
return
print(f'clean_wps_custom_data: 发现 WPS 私有数据(item 编号 {wps_item_nums}),正在清理...')
# 第三步:逐文件处理,重新打包 ZIP
for fname in file_list:
if fname in files_to_delete:
continue # 跳过 WPS 私有文件,不写入新 zip
with 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}')
调用方式
python
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 元素标签。注意:
python
# ❌ 错误:[^/]* 会在 ContentType 属性值中的 / 处提前停止
r'<Override[^>]*PartName="..."[^/]*/>'
# ✅ 正确:[^>]* 只排除 >,允许属性值中包含 /
r'<Override[^>]*PartName="..."[^>]*/>'
ContentType="application/vnd.openxmlformats-officedocument.customXmlProperties+xml" 中包含 /,必须使用 [^>]* 才能正确匹配到 />。
只清理 WPS 私有项,保留其他 customXml
文档中可能存在合法的 customXml 数据(如 APA 书目数据),清理时通过检测 wps.cn 域名精确定位,只删除 WPS 私有项,不影响其他数据。
六、验证方法
用 Python 对清理前后的文件进行验证:
python
import zipfile
def 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.rels
with 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 False
print("✓ 清理验证通过,无 WPS 私有数据残留")
return True
七、总结
| 项目 | 内容 |
|---|---|
| 问题根因 | WPS 创建的模板内嵌私有 wpsCustomData schema,Microsoft Word 无法识别 |
| 触发条件 | 用 python-docx 加载 WPS 模板并保存,私有数据被保留到输出文件 |
| 修复方式 | doc.save() 后对 ZIP 包进行后处理,删除 WPS 私有文件并清理引用 |
| 清理范围 | customXml/item{n}.xml、customXml/itemProps{n}.xml、customXml/_rels/item{n}.xml.rels、[Content_Types].xml 中的 Override 声明、word/_rels/document.xml.rels 中的 Relationship 条目 |
| 副作用 | 无,不影响文档内容和其他合法 customXml 数据 |
适用场景:所有使用 python-docx 基于 WPS 创建的模板生成 Word 文档的场景,均可能遇到此问题,使用本文的清理方案可彻底解决。

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