• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Статья Объединение документов формата Microsoft Word в один с помощью Python

Не могу сказать за всех, а у меня иногда возникает необходимость в объединении нескольких файлов формата «.docx» в один документ. И зачастую это приходиться делать вручную, что занимает довольно много времени, так как большая его часть уходит на открытие документа, копирование и вставку в объединенный документ. Чтобы ускорить данный процесс я попробовал написать небольшой скрипт на Python, который делает все указанные операции в автоматическом режиме.

000.jpg

Первым делом, при решении данной задачи приходит на ум использование библиотеки python-docx. И да, она может помочь решить задачу частично. То есть, если ваши потребности невелики, а документ не содержит картинок и таблиц, то можно воспользоваться решением на основе данной библиотеки. Конечно же, нельзя сказать, что с ее помощью нельзя перенести документ полностью. Однако, здесь потребуется написать отдельный модуль для анализа содержимого документа, который будет определять, какой тип блоков присутствует и на основе этого формировать новый документ. Этого мы делать не будем, так как есть несколько более простое решение. Но, для полноты картины давайте рассмотрим, как можно объединить текст в нескольких документах с помощью python-docx.


Что потребуется?

Для работы данного скрипта потребуется установить библиотеку python-docx. Именно с ее помощью будут выполняться все основные операции по объединению. Чтобы установить данную библиотеку, пишем в терминале команду:

pip install python-docx

После того, как библиотека будет установлена, импортируем нужные модули в наш скрипт.

Python:
import os
import sys
from pathlib import Path

from docx import Document


Объединение документов

Создадим функцию merge(path: str, name: str). На входе она будет принимать путь к директории с документами для объединения, а также имя для объединенного документа. Для начала создадим новый документ, куда и будем копировать текстовое содержимое из остальных документов. Теперь в цикле итерируемся по директории с документами. Проверяем, является ли текущий файл документом «.docx». Открываем текущий документ.

Python:
def merge(path: str, name: str):
    master = Document()
    for f in os.listdir(path):
        if Path(f).suffix == '.docx':
            doc = Document(Path(path) / f)

Теперь в цикле итерируемся по параграфам открытого документа и добавляем в объединенный документ параграф. Затем добавляем в созданный документ текст из открытого документа, а также применяем стили, которые содержит открытый документ к вставленному тексту. После чего итерируемся дальше. Как только закончатся документы в директории, сохраняем объединенный документ в директорию со скриптом под введенным пользователем именем.

Python:
            for p in doc.paragraphs:
                out_para = master.add_paragraph()
                for run in p.runs:
                    output_run = out_para.add_run(run.text)
                    output_run.bold = run.bold
                    output_run.italic = run.italic
                    output_run.underline = run.underline
                    output_run.font.color.rgb = run.font.color.rgb
                    output_run.style.name = run.style.name
    master.save(name)

Python:
def merge(path: str, name: str):
    master = Document()
    for f in os.listdir(path):
        if Path(f).suffix == '.docx':
            doc = Document(Path(path) / f)
            for p in doc.paragraphs:
                out_para = master.add_paragraph()
                for run in p.runs:
                    output_run = out_para.add_run(run.text)
                    output_run.bold = run.bold
                    output_run.italic = run.italic
                    output_run.underline = run.underline
                    output_run.font.color.rgb = run.font.color.rgb
                    output_run.style.name = run.style.name
    master.save(name)


Функция main

Создадим функцию main, в которой будем запрашивать у пользователя директорию с документами для объединения. Проверять ее на существование. Затем запрашивать имя для объединенного документа и запускать функцию по объединению, передав туда полученные данные. После того, как объединение будет завершено, сообщаем об этом пользователю.

Python:
def main():
    path = input("Введите путь к директории с документами для объединения: ")
    if not Path(path).exists():
        print("Директории не существует")
        sys.exit(0)
    name = f'{input("Введите имя для объединенного документа: ")}.docx'
    merge(path, name)
    print(f"Объединение документов завершено. Документ сохранен -> {Path.cwd() / name}")


if __name__ == "__main__":
    main()

Вот, для примера, объединенный документ.

01.png

Однако здесь отсутствует важная деталь, а именно, изображение, которое здесь присутствует в оригинале.

02.png

Как я уже говорил выше, вышеприведенный скрипт объединяет только текст.
И если в документе нет изображений или таблиц, этого будет достаточно.

Python:
# pip install python-docx

import os
import sys
from pathlib import Path

from docx import Document


def merge(path: str, name: str):
    master = Document()
    for f in os.listdir(path):
        if Path(f).suffix == '.docx':
            doc = Document(Path(path) / f)
            for p in doc.paragraphs:
                out_para = master.add_paragraph()
                for run in p.runs:
                    output_run = out_para.add_run(run.text)
                    output_run.bold = run.bold
                    output_run.italic = run.italic
                    output_run.underline = run.underline
                    output_run.font.color.rgb = run.font.color.rgb
                    output_run.style.name = run.style.name
    master.save(name)


def main():
    path = input("Введите путь к директории с документами для объединения: ")
    if not Path(path).exists():
        print("Директории не существует")
        sys.exit(0)
    name = f'{input("Введите имя для объединенного документа: ")}.docx'
    merge(path, name)
    print(f"Объединение документов завершено. Документ сохранен -> {Path.cwd() / name}")


if __name__ == "__main__":
    main()


Но что, если все названные элементы присутствуют в исходниках? Давайте посмотрим, можно ли объединить документы, сохранив при этом изображения и таблицы.


Что потребуется?

На этот раз нам потребуются уже две библиотеки для работы с документами «docx». Первую мы уже рассматривали, это python-docx. А вот вторая библиотека – docxcompose. Она то как раз и предназначена для корректного объединения документов.

Оговорюсь сразу - python-docx здесь вовсе не обязательна и используется только для примера. То есть, с ее помощью мы будем создавать документ, в который поместим объединенные данные. Однако, в качестве такого документа может служить и первый документ из директории. К примеру, у вас есть несколько файлов: док1.docx, док2.docx, док3.docx и так далее. Вы хотите объединить эти документы. То есть, по сути, перенести все данные в первый документ. Он, в данном случае и будет документом для объединения данных, куда будет добавляться все остальное.

Для установки вышеуказанных библиотек пишем в терминале команду:

pip install docxcompose python-docx

После установки импортируем нужные в библиотеки в скрипт:

Python:
import os
import sys
from pathlib import Path

from docx import Document
from docx.shared import Inches
from docxcompose.composer import Composer
from docx import Document as Document_compose


Создание документа для объединения

Создадим функцию create_master_docx(path: Path). На вход она принимает полный путь для сохранения созданного документа. Создаем новый документ. Выставляем нужные вам отступы. Здесь они выставляются в дюймах. Но доступны также сантиметры и пункты. Обратите внимание, что во всем объединенном документе будут применены отступы, которые будут созданы в документе для объединения. После того, как установили отступы, сохраняем документ.

Python:
def create_master_docx(path: Path):
    """
    Создаем и сохраняем пустой документ.
    """
    document = Document()
    section = document.sections[-1]
    section.top_margin = Inches(0.8)
    section.bottom_margin = Inches(0.8)
    section.left_margin = Inches(1.2)
    section.right_margin = Inches(1.2)
    document.save(path)


Объединение документа

Создадим функцию для объединения документа merge_docx(path_master: Path, files: list). На вход она принимает путь к созданному документу для объединения и список с документами, которые нужно объединить. Данный список будет формироваться на основе содержимого определенной директории, которую мы будем указывать при запуске скрипта.
Получаем количество файлов для объединения. Открываем документ, в который будут помещаться данные. В нашем случае мы его создали ранее. И создаем на основе открытого документа объект композитора.
Затем в цикле итерируемся по файлам в указанной директории. Открываем текущий файл, добавляем его содержимое в композитор. После того, как закончится итерация, сохраняем объединенный документ.

Python:
def merge_docx(path_master: Path, files: list):
    """
    Открываем файл в который будем объединять все остальные.
    Создаем композитора. Запускаем цикл по списку файлов.
    Добавляем в композитор содержимое каждого файла из списка.
    Сохраняем итоговый документ.
    """
    number_of_sections = len(files)
    master = Document_compose(path_master)
    composer = Composer(master)
    for i in range(0, number_of_sections):
        doc_temp = Document_compose(files[i])
        composer.append(doc_temp)
    composer.save(path_master)

Хотел бы обратить ваше внимание, что перед объединением документов, в каждый из них, в самый конец, лучше всего добавить отступ, чтобы при объединении не сливался текст. Если этого не сделать, текст будет идти подряд, без пробелов.


Запрос параметров у пользователя

В данной функции мы запрашиваем у пользователя параметры, которые будут необходимы при объединении документов, а именно имя для объединенного файла и путь к директории с файлами для объединения. При необходимости, у пользователя можно также запрашивать отступы для создаваемого нового документа.

После того, как пользователь введет необходимые данные, проверяем путь к введенной им директории. После чего составляем список файлов «.docx» с полными путями. Проверяем, есть ли хоть что-то в составленном списке. Если да, создаем новый документ. А затем запускаем функцию объединения документов. После завершения работы функции сообщаем пользователю о сохраненном объединенном документе.

Python:
def main():
    """
    Запрашиваем имя для объединенного файла.
    Запрашиваем путь к директории с файлами для объединения.
    Проверяем существование директории и является ли переданный путь путем к директории.
    Получаем список файлов в директории, формируем список файлов с полным путем к ним.
    Создаем новый документ.
    Объединяем документы из директории.
    """
    path = Path.cwd() / f'{input("Введите имя для объединенного файла: ")}.docx'
    merge_dir = input("Введите путь к директории с документами для объединения: ")
    if not Path(merge_dir).exists() or not Path(merge_dir).is_dir():
        print("Директории не существует")
        sys.exit(0)
    files = [Path(merge_dir) / x for x in os.listdir(merge_dir) if Path(x).suffix == ".docx"]
    if files:
        create_master_docx(path)
        merge_docx(path, files)
        print(f"Объединение завершено. Объединенный файл -> {path}")
    else:
        print("Файлов для объединения не найдено")


if __name__ == "__main__":
    main()


Python:
# pip install docxcompose python-docx

import os
import sys
from pathlib import Path

from docx import Document
from docx.shared import Inches
from docxcompose.composer import Composer
from docx import Document as Document_compose


def create_master_docx(path: Path):
    """
    Создаем и сохраняем пустой документ.
    """
    document = Document()
    section = document.sections[-1]
    section.top_margin = Inches(0.8)
    section.bottom_margin = Inches(0.8)
    section.left_margin = Inches(1.2)
    section.right_margin = Inches(1.2)
    document.save(path)


def merge_docx(path_master: Path, files: list):
    """
    Открываем файл в который будем объединять все остальные.
    Создаем композитора. Запускаем цикл по списку файлов.
    Добавляем в композитор содержимое каждого файла из списка.
    Сохраняем итоговый документ.
    """
    number_of_sections = len(files)
    master = Document_compose(path_master)
    composer = Composer(master)
    for i in range(0, number_of_sections):
        doc_temp = Document_compose(files[i])
        composer.append(doc_temp)
    composer.save(path_master)


def main():
    """
    Запрашиваем имя для объединенного файла.
    Запрашиваем путь к директории с файлами для объединения.
    Проверяем существование директории и является ли переданный путь путем к директории.
    Получаем список файлов в директории, формируем список файлов с полным путем к ним.
    Создаем новый документ.
    Объединяем документы из директории.
    """
    path = Path.cwd() / f'{input("Введите имя для объединенного файла: ")}.docx'
    merge_dir = input("Введите путь к директории с документами для объединения: ")
    if not Path(merge_dir).exists() or not Path(merge_dir).is_dir():
        print("Директории не существует")
        sys.exit(0)
    files = [Path(merge_dir) / x for x in os.listdir(merge_dir) if Path(x).suffix == ".docx"]
    if files:
        create_master_docx(path)
        merge_docx(path, files)
        print(f"Объединение завершено. Объединенный файл -> {path}")
    else:
        print("Файлов для объединения не найдено")


if __name__ == "__main__":
    main()

Данный скрипт отрабатывает уже более корректно. Переносятся, помимо текста все изображения и таблицы.
Вот такие вот два способа объединения документов «.docx», которыми я хотел с вами поделиться. Скажем так, конечно же, данные скрипты далеки от идеала, но позволяют немного автоматизировать рутинную деятельность.

А на этом, пожалуй, все.

Спасибо за внимание. Надеюсь, данная информация будет вам полезна
 

Вложения

  • merge_docx.zip
    1,9 КБ · Просмотры: 160
Последнее редактирование модератором:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!