объединить текстовый документ, используя python docx

У меня есть несколько текстовых файлов, каждый из которых имеет определенное содержимое. Я хотел бы получить фрагмент, который покажет мне или поможет мне понять, как объединить файлы слов в один файл при использовании библиотеки Python docx.

Например, в библиотеке 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


person omri_saadon    schedule 21.07.2014    source источник
comment
я снова написал вопрос @abarnert   -  person omri_saadon    schedule 21.07.2014
comment
Вопрос в переписанном виде выглядит очень ответным. Спасибо @omri_saadon   -  person Adam Smith    schedule 21.07.2014
comment
@AdamSmith: Да, можно ответить, но теперь он просит нас перенести его код из одной библиотеки в другую, что по-прежнему не подходит для SO. Тем более, что он не показал ни одного из своего кода docx и не описал, как далеко он продвинулся и где он застрял, кроме как в самых расплывчатых выражениях.   -  person abarnert    schedule 21.07.2014
comment
Я не знаю, как это сделать, моя идея заключалась в том, чтобы пробежаться по каждому документу (пробежаться по абзацам и таблицам) и каким-то образом скопировать его в новый файл слова. даже если у вас есть общее представление о том, как это сделать, я буду более чем рад. Я знаком с этой библиотекой несколько дней. @абарнерт –   -  person omri_saadon    schedule 22.07.2014


Ответы (7)


Если ваши потребности просты, что-то вроде этого может работать:

source_document = Document('source.docx')
target_document = Document()

for paragraph in source_document.paragraphs:
    text = paragraph.text
    target_document.add_paragraph(text)

Есть дополнительные вещи, которые вы можете сделать, но это должно помочь вам начать.

Оказывается, копирование содержимого из одного файла Word в другой в общем случае довольно сложно, включая такие вещи, как согласование стилей, присутствующих в исходном документе, которые, например, могут конфликтовать в целевом документе. Так что, скажем, в следующем году мы вряд ли добавим эту функцию.

person scanny    schedule 22.07.2014
comment
он также скопирует таблицы? @сканни - person omri_saadon; 22.07.2014
comment
Нет. См. эту страницу для обсуждения, связанного с этим: github.com/python- openxml/python-docx/issues/40 - person scanny; 22.07.2014
comment
мне удалось скопировать все в новый файл docx, но все форматы исчезли (например, полужирный). есть способ их сохранить? - person omri_saadon; 24.07.2014
comment
Ну, как я уже сказал, решение задачи в общем случае сложное. Вероятно, вы могли бы добиться некоторого прогресса, спустившись на уровень выполнения и сопоставив там полужирный и курсив. Каждый абзац состоит из серий (в первом приближении), а форматирование символов живет на уровне серий. - person scanny; 25.07.2014

Альтернативный подход к объединению двух документов, включая все стили, заключается в использовании библиотеки Python docxcompose ( https://pypi.org/project/docxcompose/). Нам не нужно явно определять стиль, и нам не нужно читать документ абзац за абзацем и присоединять его к основному документу. Использование docxcompose python показано в приведенном ниже коде.

#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
person Shashank Shekhar Shukla    schedule 17.02.2019
comment
Это довольно старый вопрос, но вы получаете мой голос за представление композитора :) - person omri_saadon; 17.02.2019
comment
@Shashank Могу ли я добавить текст из одного документа в определенное место в другом документе, используя docxcompose? - person Nemo; 13.03.2020
comment
@Nemo Я не уверен, сможем ли мы добиться этого с помощью docxcompose, поскольку предоставляемая ими документация очень ограничена. Но я уверен, что это можно сделать, рассматривая документ как пакет OOXML, как указано в ответе, написанном yunshi в этой теме. - person Shashank Shekhar Shukla; 15.03.2020
comment
@ShashankShekharShukla Я должен сказать вам большое спасибо. Ваш комментарий ценен. - person toombeos; 07.05.2020
comment
@toombeos рад слышать, что это помогло вам - person Shashank Shekhar Shukla; 07.05.2020

Я настроил приведенный выше пример для работы с последней версией 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)
person maerteijn    schedule 08.11.2016
comment
Да, но актуально :) - person maerteijn; 08.11.2016
comment
Это было очень полезно, спасибо. В моем случае мне приходилось иметь дело с большим количеством пользовательских стилей (но они были одинаковыми для всех документов), поэтому было проще использовать первый документ в списке как merged_document, а затем добавлять к нему все остальные. Таким образом, нет конфликтов в стиле с шаблоном по умолчанию, который Document() использует по умолчанию. - person Mr Kriss; 14.06.2017
comment
Хорошее решение. Обратите внимание, что append в логике python-docx означает ВЫРЕЗАТЬ и вставить, а не КОПИРОВАТЬ и вставить, поэтому, если вы используете модифицированную версию вышеперечисленного, которая принимает существующий документ (как я), вам нужно сначала сохранить документ во временном файле. путь, создайте новый документ из этого пути и затем очистите временный путь (лучший способ, который я мог найти, чтобы клонировать документ). - person Luke Sawczak; 16.09.2020

Создайте пустой документ (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)
person John Paul Hayes    schedule 23.11.2014
comment
AttributeError: объект «Документ» не имеет атрибута «_document_part»? - person coachcal; 14.09.2016
comment
@coachcal _document_part является частным, и не следует обращаться к нему через API. В любом случае это зависит от версии/реализации. Например. может исчезнуть с Python3. Попробуйте решение Мартина Джейкобса. Это выглядит очень похоже, но не использует частные члены. Я только что попробовал это, и это сработало (Python 3.5.3). - person Adrian W; 13.06.2018

Если вам просто нужно объединить простые документы только с текстом, вы можете использовать python-docx, как указано выше.

Если вам нужно объединить документы, содержащие гиперссылки, изображения, списки, маркеры и т. д., вы можете сделать это, используя lxml, чтобы объединить тело документа и все справочные файлы, например:

  • слово/styles.xml
  • слово/нумерация.xml
  • слово/медиа
  • [Типы_содержимого].xml
person yunshi    schedule 25.03.2015
comment
Звучит многообещающе. Не могли бы вы привести пример, как это сделать? Можно как отдельный Вопрос+Ответ? Большое спасибо. - person Adrian W; 13.06.2018

Это все очень полезно. Я объединил ответы Мартина Джейкобса и мистера Крисса.

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
person MZA    schedule 25.09.2017
comment
Вам не нужно предложение if filnr < len(input_files)-1:, если вы переместите merged_document.add_page_break() в начало дерева else. Затем вы будете вставлять разрыв страницы перед каждым документом, кроме первого. - person Adrian W; 13.06.2018
comment
Текст верхних и нижних колонтитулов повторяется трижды! - person Nida Sahar; 28.11.2019

Другим альтернативным решением является Aspose.Words Cloud SDK для Python. Он сохраняет форматирование/стиль документов на основе параметра 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)

#download file
request_download=asposewordscloud.models.requests.DownloadFileRequest(remoteFolder + '/' + remoteFileName)
response_download = words_api.download_file(request_download)
copyfile(response_download, localFolder + '/' +"Append_output.docx")
person Tilal Ahmad    schedule 12.01.2021