用python-docx在docx文件中替换文本并保存更改后的文件

12
我正在尝试使用python-docx模块替换文件中的单词,并保存新文件,但新文件必须与旧文件具有完全相同的格式。我该如何做到这一点?
docx模块有一个savedocx函数,它需要7个输入参数:
- document - coreprops - appprops - contenttypes - websettings - wordrelationships - output
我该如何保持原始文件中的所有内容不变,仅替换单词?

你尝试了什么?你还可以通过提供模块链接来获得更多帮助。 - Tiago
1
https://github.com/mikemaccana/python-docx - Lucas Kauffman
几乎所有这些参数都有很好的默认值。你可以忽略除了 outputdocument 以外的其他内容。 - Mad Physicist
8个回答

12

这个对我有用:

rep = {'a': 'b'}  # lookup dictionary, replace `a` with `b`
def docx_replace(old_file,new_file,rep):
    zin = zipfile.ZipFile (old_file, 'r')
    zout = zipfile.ZipFile (new_file, 'w')
    for item in zin.infolist():
        buffer = zin.read(item.filename)
        if (item.filename == 'word/document.xml'):
            res = buffer.decode("utf-8")
            for r in rep:
                res = res.replace(r,rep[r])
            buffer = res.encode("utf-8")
        zout.writestr(item, buffer)
    zout.close()
    zin.close()

哇,这太棒了!我一直在苦苦寻找这样的一个函数。谢谢你! - khu
请您能详细说明一下吗? - Arnav Mehta
是的,rep应该是什么?您能发布一个函数调用示例以查看传递的rep值格式吗?谢谢。 - abu
rep是一个查找字典,例如rep = {'a': 'b'}。 - Ramil Gataullin

8
似乎,Python的Docx并不是为了存储完整的包含图像、页眉等内容的Docx文件,而只包含文档的内部内容。因此,没有简单的方法可以做到这一点。
但是,您可以按照以下步骤操作:
首先,请查看docx标签wiki:
它解释了如何解压缩docx文件:这是一个典型文件的样子:
+--docProps
|  +  app.xml
|  \  core.xml
+  res.log
+--word //this folder contains most of the files that control the content of the document
|  +  document.xml //Is the actual content of the document
|  +  endnotes.xml
|  +  fontTable.xml
|  +  footer1.xml //Containst the elements in the footer of the document
|  +  footnotes.xml
|  +--media //This folder contains all images embedded in the word
|  |  \  image1.jpeg
|  +  settings.xml
|  +  styles.xml
|  +  stylesWithEffects.xml
|  +--theme
|  |  \  theme1.xml
|  +  webSettings.xml
|  \--_rels
|     \  document.xml.rels //this document tells word where the images are situated
+  [Content_Types].xml
\--_rels
   \  .rels

opendocx方法只获取文档的一部分,仅限于Docx格式。

def opendocx(file):
    '''Open a docx file, return a document XML tree'''
    mydoc = zipfile.ZipFile(file)
    xmlcontent = mydoc.read('word/document.xml')
    document = etree.fromstring(xmlcontent)
    return document

它只获取document.xml文件。
我建议你这样做:
1.使用opendocx获取文档的内容 2.使用advReplace方法替换document.xml 3.将docx作为zip打开,并用新的xml内容替换document.xml的内容。 4.关闭并输出压缩文件(将其重命名为output.docx)
如果您安装了node.js,请注意,我已经开发了一个名为DocxGenJS的模板引擎,用于处理docx文档,该库正在积极开发中,并将很快发布为一个node模块。

1
谢谢,我又搜索了一下你提供的步骤,找到了这个问题,它展示了如何实现:https://dev59.com/DmQn5IYBdhLWcg3wZGZ4 - user2617248

3

您是否正在使用来自此处的docx模块?

如果是,则docx模块已经公开了像replace、advReplace等方法,这些方法可以帮助您完成任务。有关所公开方法的更多详细信息,请参阅源代码


1
是的,我正在使用那里的模块。我使用了replace来进行更改,但我遇到的问题是我不知道如何将更改保存到文件中。这是我的代码:1)document = opendocx('sample.docx')2)replace(document,“todo”,“tada”)3)那么我应该怎么做才能将更改保存到sample.docx文档中并保持相同的原始格式? - user2617248

2
from docx import Document
file_path = 'C:/tmp.docx'
document = Document(file_path)

def docx_replace(doc_obj, data: dict):
    """example: data=dict(order_id=123), result: {order_id} -> 123"""
    for paragraph in doc_obj.paragraphs:
        for key, val in data.items():
            key_name = '{{{}}}'.format(key)
            if key_name in paragraph.text:
                paragraph.text = paragraph.text.replace(key_name, str(val))
    for table in doc_obj.tables:
        for row in table.rows:
            for cell in row.cells:
                docx_replace(cell, data)

docx_replace(document, dict(order_id=123, year=2018, payer_fio='payer_fio', payer_fio1='payer_fio1'))
document.save(file_path)

看起来不需要 key_name。只需要 if key in paragraph.text: 就可以了。但是格式似乎会丢失。 - Finix

2
上述方法的问题在于它们会丢失现有的格式。请参见我的 答案,它可以执行替换并保留格式。
此外,还有一个名为 python-docx-template 的工具,它允许在 docx 模板中使用 jinja2 样式模板。这是一个链接到 文档 的链接。

谢谢,这对我来说非常完美,尤其是针对一个相当复杂的文档。 - Pavin Joseph

1
我已经复制了一个名为python-docx的repo这里,它保留了docx文件中所有现有的数据,包括格式。希望这是你要找的内容。

1
除了 @ramil 之外,您还需要在将字符作为字符串值放入 XML 之前对其进行转义,因此以下内容适用于我:
def escape(escapee):
  escapee = escapee.replace("&", "&")
  escapee = escapee.replace("<", "&lt;")
  escapee = escapee.replace(">", "&gt;")
  escapee = escapee.replace("\"", "&quot;")
  escapee = escapee.replace("'", "&apos;")
return escapee

0
我们可以使用python-docx在docx中保留图像。 docx将图像识别为段落。 但是对于这个段落,文本为空。 所以你可以像这样使用。 paragraphs = document.paragraphs for paragraph in paragraphs: if paragraph.text == '': continue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接