用Python合并word文档 docx

19 人关注

我有几个word文件,每个都有特定的内容。我希望有一个片段能告诉我或帮助我找出如何在使用Python docx 库的情况下将这些word文件合并成一个文件。

例如,在pywin32库中我做了以下工作。

rng = self.doc.Range(0, 0)
for d in data:
    time.sleep(0.05)
    docstart = d.wordDoc.Content.Start
    self.word.Visible = True
    docend = d.wordDoc.Content.End - 1
    location = d.wordDoc.Range(docstart, docend).Copy()
    rng.Paste()
    rng.Collapse(0)
    rng.InsertBreak(win32.constants.wdPageBreak)

但我需要在使用Python的docx库而不是win32.client时进行。

4 个评论
我又写了一遍问题 @abarnert
重新写的问题看起来很有答案。谢谢你 @omri_saadon
@AdamSmith:可以回答,是的,但现在他要求我们把他的代码从一个库移植到另一个库,这对SO来说仍然不合适。特别是他没有展示任何他的docx代码,也没有描述他已经走了多远,以及他在哪里被卡住了,只能用最模糊的语言。
我不知道该怎么做,我的想法是把每个文件都过一遍(过段落和表格),然后以某种方式复制到新的word文件中。即使你有一个大致的想法,我也很高兴。@abarnert -
python
python-2.7
python-docx
omri_saadon
omri_saadon
发布于 2014-07-22
7 个回答
Shashank Shekhar Shukla
Shashank Shekhar Shukla
发布于 2021-01-13
已采纳
0 人赞同

另一种方法是使用python库中的docxcompose()来合并两个文件,包括所有的样式。 https://pypi.org/project/docxcompose/ ) .我们不需要明确地定义样式,也不需要逐段阅读文件并将其附加到主文件中。python docxcompose的用法如下代码所示

#Importing the required packages
from docxcompose.composer import Composer
from docx import Document as Document_compose
#filename_master is name of the file you want to merge the docx file into
master = Document_compose(filename_master)
composer = Composer(master)
#filename_second_docx is the name of the second docx file
doc2 = Document_compose(filename_second_docx)
#append the doc2 into the master using composer.append function
composer.append(doc2)
#Save the combined docx with a name
composer.save("combined.docx")

如果你想把多个文件合并成一个docx文件,你可以使用以下功能

#Filename_master is the name of the file you want to merge all the document into #files_list is a list containing all the filename of the docx file to be merged def combine_all_docx(filename_master,files_list): number_of_sections=len(files_list) master = Document_compose(filename_master) composer = Composer(master) for i in range(0, number_of_sections): doc_temp = Document_compose(files_list[i]) composer.append(doc_temp) composer.save("combined_file.docx") #For Example #filename_master="file1.docx" #files_list=["file2.docx","file3.docx","file4.docx",file5.docx"] #Calling the function #combine_all_docx(filename_master,files_list) #This function will combine all the document in the array files_list into the file1.docx and save the merged document into combined_file.docx
It's pretty old question, but you get my upvote for representing the compser :)
@ShashankShekharShukla 我不得不说非常感谢你。你的评论很有价值。
这正是我所寻找的,请收下我的赞许票!
这个解决方案比其他迭代元素的主体要好,因为它能正确地处理图像!
如果有人想在新的页面上添加第二个文件,只需在上述代码的 composer = Composer(master) 前添加 master.add_page_break() 即可。 不确定的是,文件的页数需要相同。
maerteijn
maerteijn
发布于 2021-01-13
0 人赞同

我对上面的例子进行了调整,以适用于最新版本的 python-docx (写作时为0.8.6)。请注意,这只是复制元素(合并元素的样式,做起来比较复杂)。

from docx import Document
files = ['file1.docx', 'file2.docx']
def combine_word_documents(files):
    merged_document = Document()
    for index, file in enumerate(files):
        sub_doc = Document(file)
        # Don't add a page break if you've reached the last file.
        if index < len(files)-1:
           sub_doc.add_page_break()
        for element in sub_doc.element.body:
            merged_document.element.body.append(element)
    merged_document.save('merged.docx')
combine_word_documents(files)
    
是的,但仍有意义 :)
这是非常有用的,谢谢。在我的案例中,我有很多自定义样式需要处理(但所有文件都是一样的),所以发现使用列表中的第一个文件作为 merged_document ,然后将所有其他文件附加到它上面会更容易。这样就不会与默认模板的样式发生冲突, Document() 默认使用的就是这个模板。
好的解决方案。请注意, append python-docx 逻辑中的意思是CUT和粘贴,而不是COPY和粘贴,所以如果你使用的是上述接受现有Document的修改版本(就像我一样),那么你需要先把Document保存到一个临时路径,从该路径实例化一个新的Document,之后再清理临时路径(我找到的克隆Document的最好方法)。
这显然工作得很好,但是,请注意,如果文件中含有图像,就不能正确地复制....,最终文件说它不能显示图像......
scanny
scanny
发布于 2021-01-13
0 人赞同

如果你的需求很简单,像这样的东西可能会有效。

source_document = Document('source.docx')
target_document = Document()
for paragraph in source_document.paragraphs:
    text = paragraph.text
    target_document.add_paragraph(text)

你还可以做其他事情,但这应该能让你开始。

事实证明,在一般情况下,从一个Word文件复制内容到另一个文件是相当复杂的,涉及到诸如协调源文件中的样式,而这些样式在目标文件中可能是冲突的。因此,我们不可能在明年增加这项功能,例如。

omri_saadon
它也会复制表格吗?@陈年旧事
不,与此相关的一些讨论见本页面。 github.com/python-openxml/python-docx/issues/40
我成功地将所有内容复制到一个新的docx文件中,但所有的格式都消失了(例如黑体)。有什么办法可以保留它们吗?
嗯,就像我说的,在一般情况下解决这个问题是很复杂的。你也许可以通过深入到运行层面并在那里匹配粗体和斜体来取得一些进展。每个段落都是由运行组成的(近似值),字符格式化存在于运行层面。
John Paul Hayes
John Paul Hayes
发布于 2021-01-13
0 人赞同

创建一个空文件(empty.docx)并将你的两个文件添加到其中。 在每个文件的循环迭代中,如有必要,添加一个分页符。

完成后,保存包含你的两个合并文件的新文件。

from docx import Document
files = ['file1.docx', 'file2.docx']
def combine_word_documents(files):
    combined_document = Document('empty.docx')
    count, number_of_files = 0, len(files)
    for file in files:
        sub_doc = Document(file)
        # Don't add a page break if you've
        # reached the last file.
        if count < number_of_files - 1:
            sub_doc.add_page_break()
        for element in sub_doc._document_part.body._element:
            combined_document._document_part.body._element.append(element)
        count += 1
    combined_document.save('combined_word_documents.docx')
combine_word_documents(files)
    
AttributeError: 'Document' object has no attribute '_document_part' ?
@coachcal _document_part 是 "私人 "和 should 不能作为API被访问。在任何情况下,这都取决于版本/实现。例如,在Python3中可能会消失。试试Martijn Jacobs的解决方案。这看起来 very 类似,但不使用私有成员。我刚刚试了一下那个,它成功了(Python 3.5.3)。
yunshi
yunshi
发布于 2021-01-13
0 人赞同

如果你只需要组合只有文本的简单文件,你可以使用上面提到的python-docx。

如果你需要合并含有超链接、图片、列表、弹出式窗口等的文件。你可以使用lxml来合并文件主体和所有的参考文件,比如。

  • word/styles.xml
  • word/numbering.xml
  • word/media
  • [Content_Types].xml
  • 这听起来很有希望。你能提供一个如何做到这一点的例子吗?可以作为单独的问题+答案吗?非常感谢。
    MZA
    MZA
    发布于 2021-01-13
    0 人赞同

    这都是非常有用的。我综合了Martijn Jacobs和Kriss先生的答案。

    def combine_word_documents(input_files):
        :param input_files: an iterable with full paths to docs
        :return: a Document object with the merged files
        for filnr, file in enumerate(input_files):
            # in my case the docx templates are in a FileField of Django, add the MEDIA_ROOT, discard the next 2 lines if not appropriate for you. 
            if 'offerte_template' in file:
                file = os.path.join(settings.MEDIA_ROOT, file)
            if filnr == 0:
                merged_document = Document(file)
                merged_document.add_page_break()
            else:
                sub_doc = Document(file)
                # Don't add a page break if you've reached the last file.
                if filnr < len(input_files)-1:
                    sub_doc.add_page_break()
                for element in sub_doc.element.body:
                    merged_document.element.body.append(element)
        return merged_document
        
    如果你把 merged_document.add_page_break() 移到 else 树的开头,你就不需要 if filnr < len(input_files)-1: 子句。然后你将插入一个分页符 before 除第一份文件外,每份文件都是如此。
    标题和foooters文本重复了三次!
    Tilal Ahmad
    Tilal Ahmad
    发布于 2021-01-13
    0 人赞同

    另一个替代解决方案是 用于Python的Aspose.Words Cloud SDK .它根据ImportFormatMode参数保留文件的格式/风格。该参数定义了哪种格式将被使用:附加的或目标文件。可能的值是KeepSourceFormatting或UseDestinationStyles。

    # For complete examples and data files, please go to https://github.com/aspose-words-cloud/aspose-words-cloud-python
    import os
    import asposewordscloud
    import asposewordscloud.models.requests
    from shutil import copyfile
    # Please get your Client ID and Secret from https://dashboard.aspose.cloud.
    client_id='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx'
    client_secret='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    words_api = asposewordscloud.WordsApi(client_id,client_secret)
    words_api.api_client.configuration.host='https://api.aspose.cloud'
    remoteFolder = 'Temp'
    localFolder = 'C:/Temp'
    localFileName = 'destination.docx'
    remoteFileName = 'destination.docx'
    localFileName1 = 'source.docx'
    remoteFileName1 = 'source.docx'
    #upload file
    words_api.upload_file(asposewordscloud.models.requests.UploadFileRequest(open(localFolder + '/' + localFileName,'rb'),remoteFolder + '/' + remoteFileName))
    words_api.upload_file(asposewordscloud.models.requests.UploadFileRequest(open(localFolder + '/' + localFileName1,'rb'),remoteFolder + '/' + remoteFileName1))
    #append Word documents
    requestDocumentListDocumentEntries0 = asposewordscloud.DocumentEntry(href=remoteFolder + '/' + remoteFileName1, import_format_mode='KeepSourceFormatting')
    requestDocumentListDocumentEntries = [requestDocumentListDocumentEntries0]
    requestDocumentList = asposewordscloud.DocumentEntryList(document_entries=requestDocumentListDocumentEntries)
    request = asposewordscloud.models.requests.AppendDocumentRequest(name=remoteFileName, document_list=requestDocumentList, folder=remoteFolder, dest_file_name= remoteFolder + '/' + remoteFileName)
    result = words_api.append_document(request)