Python-docx: автоматизация обработки Word документов
Основные возможности python-docx
Библиотека python-docx предназначена для создания, чтения и модификации документов формата DOCX. Она не требует установленного Microsoft Office и позволяет автоматизировать работу с текстом, таблицами, изображениями и стилями.
Базовое создание документа с текстом
Как создать простой документ и сохранить его?
from docx import Document
doc = Document()
doc.add_paragraph('Привет, мир!')
doc.save('example.docx')
Python docx библиотека (библиотека python-docx для работы с документами)
Этот код создаёт пустой документ, добавляет один абзац и сохраняет файл. Важно: путь к файлу указывается относительно рабочей директории, иначе документ не будет найден.
Типичная ошибка: если папка назначения не существует, python-docx выдаст ошибку FileNotFoundError. Перед сохранением следует убедиться, что каталог существует.
Чтение существующего документа
Как извлечь текст из файла .docx?
from docx import Document
doc = Document('existing.docx')
for paragraph in doc.paragraphs:
print(paragraph.text)
Python документ word (работа с документами word в python (python-docx))
Метод paragraphs возвращает список всех абзацев документа. Для извлечения текста из таблиц нужно дополнительно перебирать строки и ячейки.
Проблема: если файл повреждён или не является DOCX, возникнет исключение docx.opc.exceptions.PackageNotFoundError. Рекомендуется проверять существование файла и его расширение.
Добавление таблицы
Как вставить таблицу с данными?
from docx import Document
doc = Document()
table = doc.add_table(rows=2, cols=3)
table.cell(0, 0).text = 'Имя'
table.cell(0, 1).text = 'Возраст'
table.cell(0, 2).text = 'Город'
table.cell(1, 0).text = 'Анна'
table.cell(1, 1).text = '25'
table.cell(1, 2).text = 'Москва'
doc.save('table_example.docx')
Таблица создаётся с указанным числом строк и столбцов. Для заполнения используется метод cell(row, col).text. Чтобы добавить границы, нужно настроить стиль таблицы (table.style = 'Light Grid Accent 1').
Ошибка: если обратиться к несуществующей ячейке (например, row=10), возникнет IndexError. Следует контролировать размеры таблицы.
Форматирование текста (шрифт, размер, жирность)
Как изменить начертание текста в абзаце?
from docx import Document
from docx.shared import Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
doc = Document()
p = doc.add_paragraph('Важный текст')
run = p.runs[0]
run.font.size = Pt(16)
run.font.bold = True
run.font.name = 'Arial'
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.save('formatted.docx')
Абзац состоит из run (непрерывный сегмент текста). Свойства шрифта задаются через объект run.font. Выравнивание устанавливается для всего абзаца.
Проблема: если текст состоит из нескольких run (например, из-за разных стилей внутри абзаца), изменение первого run может не повлиять на остальные. Нужно перебирать все runs.
Вставка изображения
Как добавить картинку в документ?
from docx import Document
from docx.shared import Inches
doc = Document()
doc.add_picture('image.png', width=Inches(4.0), height=Inches(3.0))
doc.save('image_doc.docx')
Изображение добавляется в текущую позицию (обычно в новый абзац). Размер можно задать в дюймах, сантиметрах (Cm) или пунктах (Pt).
Ошибка: если файл изображения не найден, возникнет FileNotFoundError. Поддерживаются форматы PNG, JPEG, GIF, BMP. Для больших изображений рекомендуется указывать точные размеры, чтобы избежать растяжения.
Работа с разделами и колонтитулами
Как задать ориентацию страницы или добавить верхний колонтитул?
from docx import Document
from docx.shared import Inches, Pt
from docx.enum.section import WD_ORIENT
doc = Document()
section = doc.sections[0]
section.orientation = WD_ORIENT.LANDSCAPE
section.page_width = Inches(11.69)
section.page_height = Inches(8.27)
header = section.header
header.is_linked_to_previous = False
hp = header.paragraphs[0]
hp.text = 'Колонтитул'
hp.style = doc.styles['Header']
doc.save('section_header.docx')
Каждый раздел имеет свои параметры страницы. Колонтитулы (верхний/нижний) доступны через section.header / section.footer. Свойство is_linked_to_previous отключает связь с предыдущим разделом.
Сложность: при изменении ориентации необходимо явно указать новую ширину и высоту, иначе размеры могут остаться от предыдущей ориентации. Также колонтитулы могут не отображаться, если не задать хотя бы один абзац.
Использование стилей и создание собственных
Как применить готовый стиль или создать свой?
from docx import Document
from docx.shared import Pt, RGBColor
from docx.enum.style import WD_STYLE_TYPE
doc = Document()
style = doc.styles['Normal']
style.font.size = Pt(12)
style.font.name = 'Calibri'
new_style = doc.styles.add_style('MyStyle', WD_STYLE_TYPE.PARAGRAPH)
new_style.font.size = Pt(14)
new_style.font.color.rgb = RGBColor(0xFF, 0x00, 0x00)
p = doc.add_paragraph('Красный текст', style='MyStyle')
doc.save('styles.docx')
Встроенные стили (Normal, Heading1 и др.) можно изменять. Новые стили создаются методом add_style. Тип стиля может быть параграф, символ (run), таблица, список.
Ошибка: если стиль с таким именем уже существует, возникнет KeyError. Перед добавлением стоит проверять наличие: if 'MyStyle' not in [s.name for s in doc.styles]: ...
Расширенные примеры использования python-docx
Создание документа с оглавлением (TOC)
python-docx не имеет встроенной поддержки оглавления (поле TOC), но можно добавить поле вручную через XML. Пример:
from docx import Document
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
doc = Document()
doc.add_heading('Глава 1', level=1)
doc.add_paragraph('Текст главы 1')
doc.add_heading('Глава 2', level=1)
doc.add_paragraph('Текст главы 2')
# Вставка поля TOC в пустой абзац
p = doc.add_paragraph()
run = p.add_run()
fldChar1 = OxmlElement('w:fldChar')
fldChar1.set(qn('w:fldCharType'), 'begin')
run._r.append(fldChar1)
run2 = p.add_run()
instrText = OxmlElement('w:instrText')
instrText.set(qn('xml:space'), 'preserve')
instrText.text = ' TOC \\o "1-3" \\h \\z \\u '
run2._r.append(instrText)
run3 = p.add_run()
fldChar2 = OxmlElement('w:fldChar')
fldChar2.set(qn('w:fldCharType'), 'end')
run3._r.append(fldChar2)
doc.save('toc_doc.docx')
После открытия в Word нужно обновить поля (Ctrl+A, F9). В коде используется работа с XML OpenXML для создания поля.
Использование закладок (bookmarks)
Для создания закладок также требуется манипуляция с XML:
from docx import Document
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
doc = Document()
p = doc.add_paragraph('Текст с закладкой')
run = p.runs[0]
# Создаём элемент bookmarkStart
bm_start = OxmlElement('w:bookmarkStart')
bm_start.set(qn('w:id'), '0')
bm_start.set(qn('w:name'), 'MyBookmark')
run._r.addprevious(bm_start)
# bookmarkEnd
bm_end = OxmlElement('w:bookmarkEnd')
bm_end.set(qn('w:id'), '0')
run._r.addnext(bm_end)
doc.save('bookmark.docx')
После вставки закладки на неё можно ссылаться из гиперссылок или макросов Word.
Объединение нескольких документов
Можно скопировать содержимое одного документа в другой, используя внутренние объекты:
from docx import Document
doc1 = Document('doc1.docx')
doc2 = Document('doc2.docx')
for element in doc2.element.body:
doc1.element.body.append(element)
doc1.save('merged.docx')
Внимание: этот способ копирует всё тело документа, включая секции и колонтитулы, что может нарушить структуру. Рекомендуется копировать только содержимое (параграфы, таблицы) через изучение структуры.
Создание сложной таблицы с объединением ячеек
Для объединения ячеек используйте методы merge:
from docx import Document
from docx.shared import Inches
doc = Document()
table = doc.add_table(rows=3, cols=3)
table.cell(0, 0).merge(table.cell(0, 1))
table.cell(0, 0).text = 'Объединённая ячейка'
table.cell(1, 0).text = 'A'
table.cell(1, 1).text = 'B'
table.cell(1, 2).text = 'C'
# Установка стиля
from docx.enum.table import WD_TABLE_ALIGNMENT
table.alignment = WD_TABLE_ALIGNMENT.CENTER
doc.save('merged_table.docx')
Метод merge принимает другую ячейку и возвращает объект объединённой ячейки. После объединения индексы строк/столбцов могут сместиться.
Заполнение шаблона (mail merge) с помощью замены текста
Простой способ: подготовить документ с плейсхолдерами вида {{name}} и заменить их:
from docx import Document
doc = Document('template.docx')
placeholders = {'{{name}}': 'Анна', '{{city}}': 'Москва'}
for paragraph in doc.paragraphs:
for key, value in placeholders.items():
if key in paragraph.text:
paragraph.text = paragraph.text.replace(key, value)
# Также замена в таблицах
for table in doc.tables:
for row in table.rows:
for cell in row.cells:
for key, value in placeholders.items():
if key in cell.text:
cell.text = cell.text.replace(key, value)
doc.save('filled.docx')
Недостаток: при такой замене теряется форматирование исходного текста (run properties). Для сохранения форматирования нужно работать с XML каждого run.
Работа с XML напрямую (пример: изменение цвета страницы)
Можно модифицировать низкоуровневые свойства документа через lxml:
from docx import Document
from docx.oxml.ns import qn
doc = Document()
section = doc.sections[0]
sectPr = section._sectPr
# Добавляем цвет фона страницы
sectPr.set(qn('w:pgBorders'), '')
# Или добавить элемент background
from lxml import etree
bg = etree.SubElement(sectPr, qn('w:background'))
bg.set(qn('w:color'), 'FFFF00')
doc.save('colored_bg.docx')
Эти манипуляции требуют знания структуры OOXML.
Создание нумерованного или маркированного списка
from docx import Document
from docx.enum.text import WD_NUMBER_STYLE
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
doc = Document()
# Для списка используем абзацы с отступом и стилем List Number
for i in range(1, 4):
p = doc.add_paragraph(f'Пункт {i}', style='List Number')
doc.save('numbered_list.docx')
Можно также создавать многоуровневые списки, настраивая numPr через XML.
Результат выполнения кода (пример вывода на консоль)
Некоторые примеры не возвращают результат в консоль, а создают файл. Для демонстрации можно использовать следующий код чтения текста:
from docx import Document
doc = Document('example.docx')
for p in doc.paragraphs:
print(p.text)
Привет, мир!