背景:使用 python-docx 基于 WPS 创建的模板生成.docx报告文件,用 Microsoft word 打开时弹出以下警告:

“Word 在 report_xxx.docx 中发现无法读取的内容。是否恢复此文档的内容?如果您信任此文档的来源,请单击"是”。"

一、问题现象

程序运行正常,生成.docx 文件内容完整,但每次用 Microsoft Word 打开都会弹出"无法读取内容"警告弹窗。点击"是"之后文档可以正常显示,但这个警告严重影响用户体验,并暗示文件存在潜在的格式问题。

二、排查过程

第一步:常规 XML 结构检查

首先怀疑是 python-docx 生成的 XML 本身存在格式问题,逐一检查:

  • word/document.xml 的 body 结构
  • 颜色值格式(#000000 vs 000000
  • 图片关系引用
  • 样式引用完整性
  • 书签配对完整性
  • 表格 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: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 中的私有数据:

<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 私有数据。

实现代码

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}')

调用方式

doc = Document(template_path)
# ... 填充内容 ...
doc.save(output_path)
clean_wps_custom_data(output_path)   # ← 紧接在 save 之后调用

五、方案要点说明

为什么不直接修改模板文件?

可以手动用 Microsoft Word 打开模板并重新保存,以去除 WPS 私有数据。但这种方式存在风险:

  • 每次模板更新后都需要手动处理
  • 在自动化流水线中无法保证

因此选择在代码层面自动清理,更加健壮。

ZIP 重打包的注意事项

.docx 本质上是一个 ZIP 文件。修改其内容的正确方式是:

  1. 读取原 ZIP 到内存(io.BytesIO
  2. 创建新 ZIP,逐文件写入(跳过或修改目标文件)
  3. 将新 ZIP 的内容写回原路径

不要直接在原 ZIP 上增删文件(Python 的 zipfile 模块不支持原地删除),否则会损坏文件结构。

正则表达式中[^>]*的选择

处理 [Content_Types].xmldocument.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 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}.xmlcustomXml/itemProps{n}.xmlcustomXml/_rels/item{n}.xml.rels[Content_Types].xml 中的 Override 声明、word/_rels/document.xml.rels 中的 Relationship 条目
副作用 无,不影响文档内容和其他合法 customXml 数据

适用场景:所有使用 python-docx 基于 WPS 创建的模板生成 Word 文档的场景,均可能遇到此问题,使用本文的清理方案可彻底解决。

以上就是使用python-docx生成的Word文档打开时弹出“无法读取内容“警告的解决方案的详细内容,更多关于python-docx生成的Word无法读取内容的资料请关注本站其它相关文章!

声明:本站(华域联盟www.cnhackhy.com)所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。