如何使用python-docx替换Word文档中的文本并保存

64 投票
11 回答
172676 浏览
提问于 2025-04-18 13:42

在同一页面提到的oodocx模块指向一个/examples文件夹,但似乎这个文件夹不存在。
我已经阅读了python-docx 0.7.2的文档,还查阅了Stackoverflow上关于这个主题的所有内容,所以请相信我已经做了我的“功课”。

我只会Python(初学者+,可能算中级),所以请不要假设我懂C、Unix、xml等其他知识。

任务:打开一个包含一行文本的ms-word 2007+文档(为了简单起见),并将该行文本中出现的字典里的任何“关键”字替换为它在字典中的值。然后关闭文档,保持其他内容不变。

文本行(例如):“我们将在海洋的房间里徘徊。”

from docx import Document

document = Document('/Users/umityalcin/Desktop/Test.docx')

Dictionary = {‘sea’: “ocean”}

sections = document.sections
for section in sections:
    print(section.start_type)

#Now, I would like to navigate, focus on, get to, whatever to the section that has my
#single line of text and execute a find/replace using the dictionary above.
#then save the document in the usual way.

document.save('/Users/umityalcin/Desktop/Test.docx')

我在文档中没有看到任何可以让我做到这一点的内容——也许是有,但我没理解,因为里面的内容没有详细解释到我能理解的程度。

我遵循了这个网站上的其他建议,尝试使用该模块的早期版本(https://github.com/mikemaccana/python-docx),据说有“像replace、advReplace这样的函数”,我这样做:在Python解释器中打开源代码,并在最后添加以下内容(这是为了避免与已经安装的0.7.2版本冲突):

document = opendocx('/Users/umityalcin/Desktop/Test.docx')
words = document.xpath('//w:r', namespaces=document.nsmap)
for word in words:
    if word in Dictionary.keys():
        print "found it", Dictionary[word]
        document = replace(document, word, Dictionary[word])
savedocx(document, coreprops, appprops, contenttypes, websettings,
    wordrelationships, output, imagefiledict=None) 

运行这个会出现以下错误信息:

NameError: name 'coreprops' is not defined

也许我在尝试做一些无法完成的事情——但如果我漏掉了什么简单的东西,我会很感激你们的帮助。

如果这有关系,我在OSX 10.9.3上使用的是Enthought的64位Canopy版本。

11 个回答

2

对于表格的情况,我需要对@scanny的回答进行一些修改,变成这样:

for table in doc.tables:
    for col in table.columns:
        for cell in col.cells:
            for p in cell.paragraphs:

这样才能正常工作。实际上,这在当前的API状态下似乎并不奏效:

for table in document.tables:
    for cell in table.cells:

这里的代码也遇到了同样的问题:https://github.com/python-openxml/python-docx/issues/30#issuecomment-38658149

20

我之前从其他回答中得到了很多帮助,但对我来说,下面的代码就像在Word中简单的查找和替换功能一样。希望这对你有帮助。

#!pip install python-docx
#start from here if python-docx is installed
from docx import Document
#open the document
doc=Document('./test.docx')
Dictionary = {"sea": "ocean", "find_this_text":"new_text"}
for i in Dictionary:
    for p in doc.paragraphs:
        if p.text.find(i)>=0:
            p.text=p.text.replace(i,Dictionary[i])
#save changed document
doc.save('./test.docx')

不过,上面的解决方案有一些限制。1)包含“find_this_text”的段落会变成没有任何格式的纯文本;2)与“find_this_text”在同一段落中的控件会被删除;3)在控件或表格中的“find_this_text”不会被更改。

35

分享一个我写的小脚本 - 它可以帮助我生成带有变量的合法 .docx 合同,同时保持原来的样式。

pip install python-docx

示例:

from docx import Document
import os


def main():
    template_file_path = 'employment_agreement_template.docx'
    output_file_path = 'result.docx'

    variables = {
        "${EMPLOEE_NAME}": "Example Name",
        "${EMPLOEE_TITLE}": "Software Engineer",
        "${EMPLOEE_ID}": "302929393",
        "${EMPLOEE_ADDRESS}": "דרך השלום מנחם בגין דוגמא",
        "${EMPLOEE_PHONE}": "+972-5056000000",
        "${EMPLOEE_EMAIL}": "example@example.com",
        "${START_DATE}": "03 Jan, 2021",
        "${SALARY}": "10,000",
        "${SALARY_30}": "3,000",
        "${SALARY_70}": "7,000",
    }

    template_document = Document(template_file_path)

    for variable_key, variable_value in variables.items():
        for paragraph in template_document.paragraphs:
            replace_text_in_paragraph(paragraph, variable_key, variable_value)

        for table in template_document.tables:
            for col in table.columns:
                for cell in col.cells:
                    for paragraph in cell.paragraphs:
                        replace_text_in_paragraph(paragraph, variable_key, variable_value)

    template_document.save(output_file_path)


def replace_text_in_paragraph(paragraph, key, value):
    if key in paragraph.text:
        inline = paragraph.runs
        for item in inline:
            if key in item.text:
                item.text = item.text.replace(key, value)


if __name__ == '__main__':
    main()

在这里输入图片描述

36

我需要找到一种方法来替代docx文件中的正则表达式。于是我参考了scanny的回答。为了处理样式,我使用了这个链接中的答案: Python docx在保持样式的同时替换段落中的字符串 我还添加了递归调用来处理嵌套表格,最终得到了这样的代码:

import re
from docx import Document

def docx_replace_regex(doc_obj, regex , replace):

    for p in doc_obj.paragraphs:
        if regex.search(p.text):
            inline = p.runs
            # Loop added to work with runs (strings with same style)
            for i in range(len(inline)):
                if regex.search(inline[i].text):
                    text = regex.sub(replace, inline[i].text)
                    inline[i].text = text

    for table in doc_obj.tables:
        for row in table.rows:
            for cell in row.cells:
                docx_replace_regex(cell, regex , replace)



regex1 = re.compile(r"your regex")
replace1 = r"your replace string"
filename = "test.docx"
doc = Document(filename)
docx_replace_regex(doc, regex1 , replace1)
doc.save('result1.docx')

要遍历字典,可以使用:

for word, replacement in dictionary.items():
    word_re=re.compile(word)
    docx_replace_regex(doc, word_re , replacement)

需要注意的是,这个解决方案只有在文档中整个正则表达式的样式完全相同时才会进行替换。

另外,如果在保存后编辑了文本,相同样式的文本可能会被分成不同的部分。例如,如果你打开一个包含“testabcd”字符串的文档,把它改成“test1abcd”并保存,尽管样式是一样的,但实际上会变成三个不同的部分:“test”、“1”和“abcd”,在这种情况下,替换“test1”就不会生效。

这是为了跟踪文档中的更改。要将其合并为一个部分,在Word中你需要去“选项”,“信任中心”,然后在“隐私选项”中取消勾选“存储随机数字以提高合并准确性”,然后保存文档。

80

更新: 有几个段落级的功能可以很好地完成这个任务,大家可以在 python-docx 的 GitHub 网站上找到。

  1. 这个功能可以 用一个替换字符串替换正则表达式匹配的内容。替换后的字符串会和匹配字符串的第一个字符格式一样。
  2. 这个功能可以 隔离某个文本段落,这样就可以对这个单词或短语应用一些格式,比如高亮显示文本中每次出现的“foobar”,或者让它变成粗体或更大的字体。

目前版本的 python-docx 没有 search() 函数或 replace() 函数。虽然这些功能的请求比较频繁,但实现起来相当复杂,所以还没有被优先处理。

不过,有些人已经成功地利用现有的功能完成了他们需要的操作。这里有一个例子。顺便说一下,这个例子和段落没有关系 :)

for paragraph in document.paragraphs:
    if 'sea' in paragraph.text:
        print paragraph.text
        paragraph.text = 'new text containing ocean'

如果想在表格中搜索,也需要使用类似的代码:

for table in document.tables:
    for row in table.rows:
        for cell in row.cells:
            for paragraph in cell.paragraphs:
                if 'sea' in paragraph.text:
                    paragraph.text = paragraph.text.replace("sea", "ocean")

如果你走这条路,可能很快就会发现其中的复杂性。如果你替换了整个段落的文本,那么任何字符级的格式,比如粗体或斜体的单词或短语都会被移除。

顺便提一下,@wnnmaw 的答案中的代码是针对旧版 python-docx 的,0.3.0 之后的版本根本无法使用。

撰写回答