使用Python从法语的Word文档中提取XML的问题:生成了非法字符


问题内容

在过去的几天中,我一直在尝试创建一个脚本,该脚本将1)从Word文档中提取XML,2)修改XML,以及3)使用新XML创建并保存新的Word文档。在许多stackoverflow用户的帮助下,我最终能够找到看起来很有希望的代码。这里是:

import zipfile
import os
import tempfile
import shutil

def getXml(docxFilename):
    zip = zipfile.ZipFile(open(docxFilename,"rb"))
    xmlString= zip.read("word/document.xml").decode("utf-8")
    return xmlString

def createNewDocx(originalDocx,xmlString,newFilename):
    tmpDir = tempfile.mkdtemp()
    zip = zipfile.ZipFile(open(originalDocx,"rb"))
    zip.extractall(tmpDir)
    with open(os.path.join(tmpDir,"word/document.xml"),"w") as f:
        f.write(xmlString)
    filenames = zip.namelist()
    zipCopyFilename = newFilename
    with zipfile.ZipFile(zipCopyFilename,"w") as docx:
        for filename in filenames:
            docx.write(os.path.join(tmpDir,filename),filename)
    shutil.rmtree(tmpDir)

getXml从中提取XMLdocxFilename作为字符串。createNewDocx接收原始Word文档,并将其XML替换为xmlString,这是原始XML的修改版本,并将生成的Word文档另存为newFilename

为了检查脚本是否按预期工作,我首先创建了一个测试文档(“
test.docx”)并运行了createNewDocx("test.docx",getXml("test.docx"),"test2.docx")。如果一切都按预期工作,则应该创建一个保存为test2.docx的相同test.docx副本。确实是这样。

然后,我对测试文档进行了更详尽的阐述,并尝试对其进行修改。而且脚本仍然有效!

然后,我自信地将脚本应用于了我实际上对修改感兴趣的Word文档template.docx。我运行createNewDocx("template.docx",getXml("template.docx"),"template2.docx"),期望脚本将生成template.docx的相同副本,但名为template2.docx。不幸的是,新的Word文档无法打开。显然,XML中存在非法字符。

我真的不明白为什么我的代码对我的测试文档有效,而不对我的实际文档有效。我会发布template.docx的XML,但其中包含个人信息。test.docx和template.docx之间的一个重要区别是template.docx是用法语编写的,因此包含诸如重音符号的特殊字符,并且撇号看起来也有所不同。我不知道这是否是造成我麻烦的原因,但我没有其他想法。


问题答案:

问题是,你不小心改变的编码word/document.xmltemplate2.docxword/document.xml(来自template.docx)最初被编码为UTF-8(这是XML文档的默认编码)。

xmlString = zip.read("word/document.xml").decode("utf-8")

但是,当您复制它时,会将template2.docx编码更改为 CP-1252 。根据有关的文档open(file, "w")

在文本模式下,如果未指定编码,则使用的编码取决于平台:调用locale.getpreferredencoding(False)以获取当前的语言环境编码。

您指示调用locale.getpreferredencoding(False)给您正在编写cp1252的编码word/document.xml

由于您没有显式添加<?xml version="1.0" encoding="cp1252"?>到的开头word/document.xml,因此Word(或任何其他XML读取器)会将其读取为 UTF-8
而不是 CP-1252 ,这将导致出现非法的XML字符错误。

因此,您要在写入时通过使用参数指定为 UTF-8 的编码:encoding``open()

with open(os.path.join(tmpDir, "word/document.xml"), "w", encoding="UTF-8") as f:
    f.write(xmlString)