[python] 基于python-docx库的Word文档自动处理全解

python-docx是一款纯Python实现的第三方库,专门用于创建和修改Microsoft Word的.docx格式文档。该库无需依赖 Microsoft Word软件即可运行,具备优秀的跨平台特性,可在Windows、Mac、Linux等系统上使用。需要注意的是,python-docx仅兼容.docx 格式(对应 Word 2007 及以上版本),不支持旧版的.doc 格式文件。.docx是基于XML的开放压缩格式,而.doc是二进制私有格式,前者体积更小、兼容性更优。

python-docx官方代码仓库见:python-docx,详细文档见:python-docx docs。截至本文撰写时,python-docx的稳定版本为1.2.0,python-docx安装命令如下:

pip install python-docx

目录

  • [1 使用说明](#1 使用说明)
    • [1.1 快速入门](#1.1 快速入门)
    • [1.2 Python-docx的核心对象](#1.2 Python-docx的核心对象)
      • [1.2.1 Document对象](#1.2.1 Document对象)
      • [1.2.2 Table对象](#1.2.2 Table对象)
      • [1.2.3 文本处理](#1.2.3 文本处理)
      • [1.2.4 Sections对象](#1.2.4 Sections对象)
      • [1.2.5 页眉页脚对象](#1.2.5 页眉页脚对象)
    • [1.3 非文本内容管理](#1.3 非文本内容管理)
      • [1.3.1 样式管理](#1.3.1 样式管理)
      • [1.3.2 批注管理](#1.3.2 批注管理)
      • [1.3.3 图片对象](#1.3.3 图片对象)
    • [1.4 综合示例](#1.4 综合示例)
  • [2 参考](#2 参考)

1 使用说明

1.1 快速入门

Python-docx中的Word文档内存对象模型采用树状结构分层映射文档元素,使得程序能够精准控制文档的内容、格式与布局。该树状结构示意如下:

shell 复制代码
Document(文档)
├── Sections(节)
├── Paragraphs(段落)
│   ├── Runs(文本片段)
│   └── InlineShapes(内联形状,如图片)
├── Tables(表格)
│   ├── Rows(行)
│   │   └── Cells(单元格)
│   │       └── Paragraphs(单元格内的段落)
└── Core Properties(文档属性)

各个对象简介如下:

核心对象 代表含义 在文档树中的位置 主要功能与说明
Document 整个Word文档对象 根对象 用于打开或创建一个.docx文件
Sections 文档的节 Document.sections 管理页面设置(如页边距、纸张方向)
Paragraph 段落 Document.paragraphsTable.cell.paragraphs 文档的基本单元由一个或多个Run组成,并拥有统一的段落格式
Run 具有相同格式的连续文本 Paragraph.runs 格式控制的最小单位,可以对Run单独设置字体、大小等字符级格式
Table 表格 Document.tables 代表文档中的一个表格,由行(Table.rows)、列(Table.columns)和单元格(Table.cell)组成
Cell 表格单元格 Table.cell(row,col) 每个Cell本身可以包含多个Paragraph,就像一个微缩文档
InlineShape 内嵌形状(如图片) Paragraph.add_picture()创建 代表文档中与文本在同一层的图形对象,最常用的是图片
Styles 样式集合 Document.styles 管理文档中定义的所有样式(如"标题1"、"正文"等)

以下代码示例描述了如何通过python-docx库快速实现文档的创建,完成段落、标题、表格、图片等内容添加及字符段落样式设置,并保存为docx文件:

python 复制代码
# 1. 初始化文档
from docx import Document
from docx.shared import Inches, Cm

# 新建空白文档,不添加参数,默认新建
doc = Document()  

# 2. 添加段落
# 默认都会把内容追加到文档的末尾
doc.add_paragraph("这是文档正文段落")  # 直接添加段落
para = doc.add_paragraph("主段落") # 该方法返回新段落的引用对象
para.insert_paragraph_before("插入在主段落上方的段落")  # 主段落的引用之前插入段落
para = doc.add_paragraph("插入在主段落下方的段落") # 主段落的引用之后插入段落

# 3. 添加标题(级别1-9,0为文档标题)
doc.add_heading("文档总标题", level=0) # 一份报告的封面标题
doc.add_heading("第一章 概述", level=1) # 一级标题
doc.add_heading("1.1 核心功能", level=2) # 二级标题

# 4. 分页符
doc.add_page_break()  # 强制后续内容另起一页

# 5. 添加表格(固定行+动态行+样式)
# 5.1 固定表格(2行2列)
table1 = doc.add_table(rows=2, cols=2)
table1.cell(0, 0).text = "姓名"
table1.cell(0, 1).text = "年龄"
table1.cell(1, 0).text = "张三"
table1.cell(1, 1).text = "25"

# 5.2 动态表格(表头+数据行)
table2 = doc.add_table(rows=1, cols=3)
# 将鼠标悬停在Word表格样式库中的表格样式缩略图上,即可找到表格样式名称
table2.style = "Light Shading Accent 1"  # 应用预设样式
headers = ["数量", "产品编号", "描述"]
for i, header in enumerate(headers):
    table2.rows[0].cells[i].text = header
# 批量添加数据
items = [(7, "1024", "毛绒小猫"), (3, "2042", "菲比精灵"), (1, "1288", "豪华项圈")]
for item in items:
    row = table2.add_row().cells
    row[0].text = str(item[0])
    row[1].text = item[1]
    row[2].text = item[2]

# 6. 添加图片(本地文件+尺寸调整)
doc.add_picture("test.png", width=Inches(1.5))  # 1.5英寸宽(自动等比缩放)
doc.add_picture("test2.jpg", width=Cm(5),height=Cm(1))  # 同时指定宽高

# 7. 段落样式(项目符号/编号)
doc.add_paragraph("项目符号列表项1", style="ListBullet")
doc.add_paragraph("项目符号列表项2", style="ListBullet")
doc.add_paragraph("编号列表项1", style="ListNumber")

# 8. 字符格式(粗体/斜体)
para = doc.add_paragraph()
para.add_run("普通文本 ").bold = False
para.add_run("粗体文本").bold = True
para.add_run(" 普通文本 ").italic = False
para.add_run("斜体文本").italic = True

# 9. 字符样式(预设样式)
para2 = doc.add_paragraph("普通文本,")
para2.add_run("带强调的文本", style="Emphasis")  # 应用"强调"样式

# 10. 保存文档
doc.save("test_output.docx")

后续内容将对python-docx的各个核心对象进行具体介绍。

1.2 Python-docx的核心对象

1.2.1 Document对象

Python-docx本质上是一个文档修改工具。即使创建新文档,其原理也是基于一个预设的空白模板进行修改。因此,文档的格式(如样式、页眉、页脚)与正文内容是分离存储的,即使清空模板文字也不会删除格式设定。以下示例展示了如何创建文档或打开已有文档:

python 复制代码
from docx import Document
from io import BytesIO, StringIO  # 用于类文件对象操作

"""演示python-docx操作文档的核心功能"""
# ===================== 1. 创建新文档并保存 =====================
print("1. 创建新文档并保存...")
# 基于默认模板创建空文档
new_doc = Document()
new_doc.add_heading('python-docx 文档操作示例', level=1)
new_doc.add_paragraph('这是通过python-docx创建的新文档')
# 保存新文档到本地
new_doc.save('新建文档.docx')
print("✅ 新文档已保存为:新建文档.docx")

# ===================== 2. 打开现有文档并修改保存 =====================
print("\n2. 打开现有文档并修改...")
# 打开刚才创建的文档
existing_doc = Document('新建文档.docx')
# 向文档追加内容
existing_doc.add_paragraph('这是打开现有文档后追加的内容')
# 另存为新文件(避免覆盖原文件)
existing_doc.save('修改后的文档.docx')
print("✅ 修改后的文档已保存为:修改后的文档.docx")

# ===================== 3. 通过类文件对象(BytesIO)读写文档 =====================
print("\n3. 通过类文件对象操作文档...")
# 3.1 读取:将本地文档读取到BytesIO流中
with open('修改后的文档.docx', 'rb') as f:  # 必须用rb(二进制读),跨平台兼容
    doc_stream = BytesIO(f.read())  # 用BytesIO而非StringIO

# 从流中打开文档
stream_doc = Document(doc_stream)
# 追加内容
stream_doc.add_paragraph('这是通过类文件对象添加的内容')

# 3.2 保存:将文档保存到BytesIO流中(不落地到本地文件)
output_stream = BytesIO()
stream_doc.save(output_stream)

# 可选:将流中的内容写入本地文件,验证结果
with open('类文件对象生成的文档.docx', 'wb') as f:
    f.write(output_stream.getvalue())
print("✅ 类文件对象操作的文档已保存为:类文件对象生成的文档.docx")

# 关闭流
doc_stream.close()
output_stream.close()

1.2.2 Table对象

Word软件中的表格功能相当复杂,这导致在使用python-docx操作表格时,往往难以提前明确表格的具体内容和结构,增加了处理的难度。本节将从简单表格到复杂表格来介绍如何使用python-docx操作表格。

规整表格

规整表格也就是一个行列数固定、无合并或省略单元格的标准表格,这是最基础的表格形式。关系型数据库表和pandas数据表都是规整表格的典型示例:

shell 复制代码
+---+---+---+
| a | b | c |
+---+---+---+
| d | e | f |
+---+---+---+
| g | h | i |
+---+---+---+

通过控制表格行列数即可创建规整表格:

python 复制代码
from docx import Document
from docx.shared import Inches

# 创建新文档
doc = Document()

# 添加3行2列的规整表格
table = doc.add_table(rows=3, cols=2)
table.style = 'Table Grid'  # 显示表格边框,便于查看

# 填充单元格内容(a-i 对应示例中的规整表格)
cell_texts = ['a', 'b', 'c', 'd', 'e', 'f']
idx = 0
for row in table.rows:
    for cell in row.cells:
        cell.text = cell_texts[idx]
        idx += 1

# 保存文档
doc.save('规整表格示例.docx')

# 读取并验证规整表格的行列数(每行单元格数相同)
print("规整表格每行的单元格数:")
for i, row in enumerate(table.rows):
    print(f"第{i+1}行:{len(row.cells)}个单元格")

合并单元格

将多个连续的横向或纵向单元格合并为一个单元格,是实际使用中非常常见的方式:

shell 复制代码
+---+---+---+   +---+---+---+
|   a   | b |   |   | b | c |
+---+---+---+   + a +---+---+
| c | d | e |   |   | d | e |
+---+---+---+   +---+---+---+
| f | g | h |   | f | g | h |
+---+---+---+   +---+---+---+

实现方式为先创建规整表格,再通过cell.merge()实现单元格的横向或纵向合并:

python 复制代码
from docx import Document

doc = Document()
table = doc.add_table(rows=2, cols=3)
table.style = 'Table Grid'

# 示例1:横向合并(第一行第一个单元格合并右侧单元格)
cell_1_1 = table.cell(0, 0)  # 第1行第1列(索引从0开始)
cell_1_2 = table.cell(0, 1)
cell_1_1.merge(cell_1_2)
cell_1_1.text = '合并单元格(横向)'

# 示例2:纵向合并(第三列前两个单元格合并)
cell_1_3 = table.cell(0, 2)
cell_2_3 = table.cell(1, 2)
cell_1_3.merge(cell_2_3)
cell_1_3.text = '合并单元格(纵向)'

# 填充其他单元格
table.cell(1, 0).text = 'd'
table.cell(1, 1).text = 'e'

# 查看合并后每行的单元格数
# 注意以下内容返回的是原始表格结构
print("合并单元格后每行的单元格数:")
for i, row in enumerate(table.rows):
    print(f"第{i+1}行:{len(row.cells)}个单元格")

doc.save('合并单元格示例.docx')

递归表格

表格处理的另一个复杂点是其递归特性,Word中的表格单元格本身可以包含一个或多个表格。可以通过_Cell.tables_Cell.iter_inner_content()检测嵌套表格:

  • _Cell.tables:直接返回单元格内包含的所有表格。
  • _Cell.iter_inner_content():按文档顺序返回单元格内的所有内容(包括段落和表格),保持表格与段落的相对位置不变。

示例代码如下:

python 复制代码
from docx import Document
from docx.shared import Inches
from docx.table import Table
from docx.text.paragraph import Paragraph

def generate_nested_table_doc():
    """生成带嵌套表格的示例Word文档(示例数据)"""
    # 1. 创建新文档
    doc = Document()

    # 2. 创建外层表格(2行2列)
    outer_table = doc.add_table(rows=2, cols=2)
    outer_table.style = 'Table Grid'  # 显示表格边框

    # 3. 填充外层表格的普通单元格
    cell_1_1 = outer_table.cell(0, 0)
    cell_1_1.text = "下方单元格包含嵌套表格"

    cell_1_2 = outer_table.cell(0, 1)
    cell_1_2.text = "普通单元格(无嵌套)"

    # 4. 关键:在 (1,0) 单元格中嵌套一个子表格
    cell_2_1 = outer_table.cell(1, 0)
    # 先给嵌套表格前加一个段落(模拟真实文档的混合内容)
    para = cell_2_1.add_paragraph("嵌套表格前的段落内容:")
    # 在单元格内创建嵌套表格(3行1列)
    inner_table = cell_2_1.add_table(rows=3, cols=1)
    inner_table.style = 'Table Grid'
    # 填充嵌套表格
    inner_table.cell(0, 0).text = "嵌套表格-行1"
    inner_table.cell(1, 0).text = "嵌套表格-行2"
    inner_table.cell(2, 0).text = "嵌套表格-行3"
    # 嵌套表格后再加一个段落
    cell_2_1.add_paragraph("嵌套表格后的段落内容")

    # 5. 填充外层表格最后一个单元格
    cell_2_2 = outer_table.cell(1, 1)
    cell_2_2.text = "普通单元格(无嵌套)"

    # 6. 保存文档(生成示例数据)
    doc.save("嵌套表格.docx")
    print("=" * 50)
    print("✅ 嵌套表格.docx")
    print("=" * 50)

def traverse_table(table, level=0):
    """
    递归遍历表格,处理嵌套表格
    :param table: 要遍历的表格对象
    :param level: 表格嵌套层级(外层为0,内层+1)
    """
    indent = "  " * level  # 缩进,方便区分层级
    print(f"\n{indent}=== 遍历【层级{level}】表格 ===")
    
    # 遍历表格的每一行
    for row_idx, row in enumerate(table.rows):
        # 遍历行中的每一个单元格
        for col_idx, cell in enumerate(row.cells):
            print(f"\n{indent}单元格 ({row_idx+1}, {col_idx+1}):")
            
            # --------------------------
            # 方法1:_Cell.tables - 直接获取单元格内的所有表格
            # --------------------------
            inner_tables = cell.tables
            if inner_tables:
                print(f"{indent}  📌 检测到 {len(inner_tables)} 个嵌套表格:")
                # 递归遍历每个嵌套表格
                for inner_table in inner_tables:
                    traverse_table(inner_table, level + 1)
            else:
                print(f"{indent}  📌 无嵌套表格")
            
            # --------------------------
            # 方法2:_Cell.iter_inner_content() - 按顺序获取单元格内所有内容(段落+表格)
            # --------------------------
            print(f"{indent}  📌 单元格内完整内容(按顺序):")
            for content in cell.iter_inner_content():
                if isinstance(content, Paragraph):
                    # 处理段落内容(过滤空段落)
                    text = content.text.strip()
                    if text:
                        print(f"{indent} [段落]:{text}")
                elif isinstance(content, Table):
                    # 遇到表格时的提示(避免重复遍历,仅演示位置)
                    print(f"{indent} [表格]:嵌套表格(层级{level+1})")

def parse_nested_tables():
    """解析生成的嵌套表格文档,演示核心方法"""
    # 1. 打开示例文档
    doc = Document("嵌套表格.docx")

    # 2. 遍历文档中的所有外层表格
    print("\n开始解析嵌套表格:")
    print("-" * 50)
    for table in doc.tables:
        traverse_table(table)

# 主程序入口
if __name__ == "__main__":
    # 生成带嵌套表格的示例文档
    generate_nested_table_doc()
    
    # 第二步:解析嵌套表格,演示_Cell.tables和iter_inner_content的使用
    parse_nested_tables()
    
    print("\n" + "=" * 50)
    print("✅ 嵌套表格解析完成!")
    print("=" * 50)

1.2.3 文本处理

在Word文档中,内容的组织逻辑分为两个主要层级:块级对象和行内对象。

  • 块级对象(如段落、表格)是占据整行的大模块,其宽度由页面边距、分栏或单元格边界决定,文本会在其左右边界内自动换行。
  • 行内对象则存在于这些大模块内部,如加粗的词语或句子,最常见的表现形式是文本片段(runs)。一个段落就是由一个或多个文本片段组成的。

相应地,格式控制也分为两类:

  • 块级属性(如缩进、间距、对齐方式)控制模块在页面上的布局位置;
  • 行内属性(如字体、加粗、颜色)则控制模块内部文字的具体外观。

下面通过Python-docx的代码示例,展示这两类格式的实际应用:

python 复制代码
from docx import Document
from docx.shared import Inches, Pt, RGBColor
from docx.enum.text import (
    WD_ALIGN_PARAGRAPH,
    WD_TAB_ALIGNMENT,
    WD_TAB_LEADER,
    WD_UNDERLINE
)
from docx.enum.dml import MSO_THEME_COLOR

# 创建一个新的 Word 文档
doc = Document()

# ==================== 1. 块级格式设置:段落布局控制 ====================
# 1.1 标题段落(居中对齐、段后间距)
title_para = doc.add_paragraph("Python-docx 文本格式示例")
title_format = title_para.paragraph_format  # 块级格式对象
title_format.alignment = WD_ALIGN_PARAGRAPH.CENTER  # 居中对齐(块级属性)
title_format.space_after = Pt(18)  # 段后间距(块级属性)

# 1.2 正文段落(缩进与行间距)
content_para = doc.add_paragraph("这是一个带缩进和自定义行间距的正文段落。")
content_format = content_para.paragraph_format
content_format.left_indent = Inches(0.5)          # 左缩进
content_format.first_line_indent = Inches(-0.25)  # 悬挂缩进
content_format.line_spacing = 1.5                # 1.5倍行间距
content_format.space_before = Pt(12)             # 段前间距

# 1.3 带制表位的段落(块级定位)
tab_para = doc.add_paragraph("姓名:\t张三\t年龄:\t25")
tab_format = tab_para.paragraph_format
tab_stops = tab_format.tab_stops
# 添加三个制表位(位置、对齐方式、前导符)
tab_stops.add_tab_stop(Inches(1.5), WD_TAB_ALIGNMENT.RIGHT, WD_TAB_LEADER.DOTS)
tab_stops.add_tab_stop(Inches(2.5), WD_TAB_ALIGNMENT.LEFT, WD_TAB_LEADER.SPACES)
tab_stops.add_tab_stop(Inches(3.5), WD_TAB_ALIGNMENT.RIGHT, WD_TAB_LEADER.DOTS)

# 1.4 分页控制(块级保持与孤行控制)
page_para1 = doc.add_paragraph("这是章节标题(确保与下一段同页)")
page_para1.paragraph_format.keep_with_next = True  # 与下段同页
page_para2 = doc.add_paragraph("这是章节内容,启用了孤行控制,避免段落首行/末行单独显示在一页。" * 20)
page_para2.paragraph_format.widow_control = True   # 避免孤行

# ==================== 2. 行内格式设置:文字外观控制 ====================
char_para = doc.add_paragraph("字符格式示例:")

# 粗体
bold_run = char_para.add_run("粗体文本")
bold_run.font.bold = True
char_para.add_run(" | ")

# 斜体
italic_run = char_para.add_run("斜体文本")
italic_run.font.italic = True
char_para.add_run(" | ")

# 下划线(双下划线)
underline_run = char_para.add_run("双下划线文本")
underline_run.font.underline = WD_UNDERLINE.DOUBLE
char_para.add_run(" | ")

# 不同字号和颜色
color_run = char_para.add_run("16号红色文本")
color_run.font.size = Pt(16)               # 字号
color_run.font.color.rgb = RGBColor(255, 0, 0)  # 颜色
char_para.add_run(" | ")

# 上标
sup_run = char_para.add_run("上标")
sup_run.font.superscript = True

# ==================== 3. 保存文档 ====================
doc.save("文本格式示例.docx")
print("文档已保存为:文本格式示例.docx")

1.2.4 Sections对象

Word引入了节(section)的概念,它指文档中具有相同页面布局(如页边距、纸张方向)的连续部分。通过分节,用户可以在同一份文档中混合使用不同版式,例如同时包含纵向页面和横向页面。此外,每个节还可以独立设置其页眉与页脚的样式。绝大多数Word文档默认仅包含一个节,通常也无需更改页面布局。然而,当确实需要对页面布局进行调整时,理解节的功能与设置方法就变得至关重要。以下示例展示了如何使用python-docx库创建包含多个节、各节拥有不同页面设置的Word文档:

python 复制代码
from docx import Document
from docx.enum.section import WD_SECTION_START
from docx.enum.section import WD_ORIENT
from docx.shared import Inches

# 1. 创建新文档并查看默认节
document = Document()
print("初始文档的节数量:", len(document.sections))  # 默认1个节

# 获取默认节并修改其基础布局(纵向,常规边距)
default_section = document.sections[0]
# 设置默认节的边距:左1.5英寸、右1英寸、上下各1英寸
default_section.left_margin = Inches(1.5)
default_section.right_margin = Inches(1)
default_section.top_margin = Inches(1)
default_section.bottom_margin = Inches(1)

# 2. 给默认节添加内容(纵向页面内容)
document.add_heading('第一节:纵向页面内容', level=1)
document.add_paragraph('这是默认节的内容,页面为纵向,边距:左1.5英寸、右1英寸。')

# 3. 添加新节(奇数页开始,横向布局)
# 创建新节,指定分节符类型为奇数页开始
new_section = document.add_section(WD_SECTION_START.ODD_PAGE)
# 修改新节的页面方向为横向(需同时交换宽高)
new_section.orientation = WD_ORIENT.LANDSCAPE
# 先交换宽高值,避免页面尺寸异常
new_width, new_height = new_section.page_height, new_section.page_width
new_section.page_width = new_width
new_section.page_height = new_height
# 设置新节的边距:左右各1.25英寸,上下各0.8英寸
new_section.left_margin = Inches(1.25)
new_section.right_margin = Inches(1.25)
new_section.top_margin = Inches(0.8)
new_section.bottom_margin = Inches(0.8)

# 4. 给新节添加内容(横向页面内容)
document.add_heading('第二节:横向页面内容', level=1)
document.add_paragraph('这是新节的内容,页面为横向,边距:左右1.25英寸、上下0.8英寸。')

# 5. 再添加一个新节(偶数页开始,纵向布局)
third_section = document.add_section(WD_SECTION_START.EVEN_PAGE)
# 恢复纵向布局
third_section.orientation = WD_ORIENT.PORTRAIT
third_section.page_width = Inches(8.5)
third_section.page_height = Inches(11)
# 设置特殊边距(添加装订线)
third_section.gutter = Inches(0.2)  # 装订线0.2英寸
third_section.header_distance = Inches(0.6)  # 页眉距离0.6英寸

# 6. 给第三个节添加内容
document.add_heading('第三节:带装订线的纵向页面', level=1)
document.add_paragraph('这一节包含0.2英寸的装订线,页眉距离页面顶部0.6英寸。')

# 7. 遍历所有节,打印关键属性
print("\n=== 所有节的关键属性 ===")
for idx, section in enumerate(document.sections):
    print(f"\n第{idx+1}节:")
    print(f"  起始类型:{section.start_type}")
    print(f"  页面方向:{section.orientation}")
    print(f"  左右边距:{section.left_margin/914400:.2f}英寸 / {section.right_margin/914400:.2f}英寸")
    print(f"  装订线:{section.gutter/914400:.2f}英寸")

# 保存文档
document.save("多节文档示例.docx")
print("\n文档已保存为:多节文档示例.docx")

1.2.5 页眉页脚对象

页眉是指显示在每一页上页边距区域的文本,与正文内容相互独立,通常用于展示文档标题、作者、创建日期或页码等上下文信息。同一文档中的页眉在各页面间基本保持一致,仅在部分内容上可能存在细微差异,例如章节标题或页码的变化。页脚在功能和特性上与页眉完全类似,唯一的区别是页脚位于页面底部。页脚与脚注不同,脚注在各页面间并不统一。以下是操作文档页眉页脚的示例代码:

python 复制代码
from docx import Document

# -------------------------- 1. 初始化文档并创建基础页眉 --------------------------
# 创建新文档
doc = Document()

# 获取文档的第一个节(新建文档默认只有1个节)
section1 = doc.sections[0]
header1 = section1.header

# 检查初始状态:新建文档的页眉默认关联到前一节页眉设置(此处无前置节,故无实际页眉)
print(f"初始页眉关联状态: {header1.is_linked_to_previous}")  # 输出: True

# 为第一节添加简单页眉(仅左侧文本)
# 直接编辑页眉的第一个段落(新建页眉默认包含1个空段落)
para1 = header1.paragraphs[0]
para1.text = "文档主标题 - 第一节页眉"
# 自动创建独立页眉定义,关联状态变为False
print(f"添加页眉后关联状态: {header1.is_linked_to_previous}")  # 输出: False

# -------------------------- 2. 添加带分区(左/中/右)的页眉 --------------------------
# 先清空第一节原有页眉内容,重新设置分区页眉
para1.clear()
# 使用制表符\t分隔左、中、右三部分内容
para1.text = "左侧:文档名称\t居中:2026年1月\t右侧:页码"
# 应用Word默认的"Header"样式(确保制表位生效)
para1.style = doc.styles["Header"]

# -------------------------- 3. 添加新节并设置独立页眉 --------------------------
# 向文档末尾添加新节(section2)
section2 = doc.add_section()
header2 = section2.header

# 初始状态:新节的页眉默认关联到前一节(section1)
print(f"第二节初始关联状态: {header2.is_linked_to_previous}")  # 输出: True
# 验证:此时编辑header2的内容会直接修改section1的页眉(继承特性)
# 先取消关联,创建第二节的独立页眉
header2.is_linked_to_previous = False
# 为第二节添加专属页眉
para2 = header2.paragraphs[0]
para2.text = "第二节独立页眉 - 仅本节显示"

# -------------------------- 4. 添加页脚(与页眉用法完全一致) --------------------------
# 为第一节添加页脚(包含页码)
footer1 = doc.sections[0].footer
footer1.is_linked_to_previous = False  # 取消关联,创建独立页脚
footer_para1 = footer1.paragraphs[0]
footer_para1.text = "\t第 {PAGE} 页 / 共 {NUMPAGES} 页\t"  # 居中显示页码
footer_para1.style = doc.styles["Footer"]
# 为第二节添加不同的页脚
footer2 = doc.sections[1].footer
footer2.is_linked_to_previous = False
footer_para2 = footer2.paragraphs[0]
footer_para2.text = "第二节页脚 - 联系人:测试账号"

# -------------------------- 5. 删除页眉(恢复关联状态) --------------------------
# 若需删除第二节的独立页眉,只需将其关联状态设为True(会永久删除内容)
# 此处仅演示,注释掉避免影响最终效果
# header2.is_linked_to_previous = True

# -------------------------- 6. 保存文档 --------------------------
doc.save("页眉页脚示例.docx")
print("文档已保存为:页眉页脚示例.docx")

1.3 非文本内容管理

1.3.1 样式管理

在Word中,内置样式会出现在Word界面的样式面板中,但不会自动加入文档中,直到你第一次使用它。这样避免了文件因所有样式定义而臃肿。一旦某样式被使用,其定义就会永久加入文档,即使后来删除应用内容也不会消失。因此,若要用python-docx使样式生效,必须在初始文档中包含其定义,否则样式会静默失效。

以下是一个综合性的示例,它整合了文档中所有核心的样式操作功能,包括样式的访问、应用、创建、删除,以及字符段落格式的定义等:

python 复制代码
from docx import Document
from docx.enum.style import WD_STYLE_TYPE
from docx.shared import Pt, Inches
from docx.enum.text import WD_UNDERLINE

# 1. 创建新文档并访问样式集合
doc = Document()
styles = doc.styles
print(f"初始样式数量: {len(styles)}")

# 2. 访问并筛选特定类型的样式
# 筛选所有段落样式并打印名称
print("\n=== 所有段落样式 ===")
paragraph_styles = [s for s in styles if s.type == WD_STYLE_TYPE.PARAGRAPH]
for style in paragraph_styles[:5]:  # 只打印前5个避免输出过长
    print(f"- {style.name}")

# 3. 应用现有样式到新段落
# 方式1: 创建时指定样式名称
doc.add_heading("样式操作示例", level=0)
heading1_para = doc.add_paragraph("这是应用 Heading 1 样式的段落", style="Heading 1")
# 方式2: 先创建段落再赋值样式对象
normal_para = doc.add_paragraph()
normal_para.text = "这是先创建再应用 Normal 样式的段落"
normal_para.style = styles["Normal"]

# 4. 创建自定义样式
# 4.1 创建段落样式并设置基础样式、字符格式、段落格式
citation_style = styles.add_style("Citation", WD_STYLE_TYPE.PARAGRAPH)
# 设置基础样式(继承 Normal 样式的格式)
citation_style.base_style = styles["Normal"]
# 设置字符格式
citation_style.font.name = "宋体"
citation_style.font.size = Pt(10)
citation_style.font.italic = True  # 斜体
citation_style.font.underline = WD_UNDERLINE.DOT_DASH  # 点划线下划线
# 设置段落格式(悬挂缩进、段前间距)
citation_style.paragraph_format.left_indent = Inches(0.25)
citation_style.paragraph_format.first_line_indent = Inches(-0.25)
citation_style.paragraph_format.space_before = Pt(6)
# 设置后续段落样式
citation_style.next_paragraph_style = styles["Normal"]

# 应用自定义样式
citation_para = doc.add_paragraph("这是自定义 Citation 样式的引用文本", style="Citation")
print(f"\n自定义样式应用后名称: {citation_para.style.name}")

# 5. 控制样式在 Word 中的显示(快速样式、优先级)
body_text_style = styles["Body Text"]
body_text_style.hidden = False
body_text_style.quick_style = True
body_text_style.priority = 1

# 6. 处理潜在样式(Latent Styles)
latent_styles = styles.latent_styles
print(f"\n潜在样式总数: {len(latent_styles)}")

# 为 List Bullet 添加潜在样式定义
if "List Bullet" not in latent_styles:
    latent_list_bullet = latent_styles.add_latent_style("List Bullet")
    latent_list_bullet.hidden = False
    latent_list_bullet.priority = 2
    latent_list_bullet.quick_style = True
print(f"List Bullet 潜在样式优先级: {latent_styles['List Bullet'].priority}")

# 7. 删除样式
print(f"\n删除前样式数量: {len(styles)}")
if "Citation" in styles:
    styles["Citation"].delete()
    print(f"删除后样式数量: {len(styles)}")

# 8. 保存文档
doc.save("样式操作示例.docx")
print("\n文档已保存为: 样式操作示例.docx")

1.3.2 批注管理

Word支持在文档中添加批注,这是审阅功能的一部分,通常用于他人在不修改原文的情况下提供反馈。Word文档里操作步骤如下:

  1. 选取文本范围;
  2. 点击审阅工具栏中的新建批注;
  3. 输入或粘贴批注内容。

批注仅能添加至文档主体,不可插入页眉、页脚或其他批注内。Word文档里脚注和尾注中理论上允许添加批注,但当前python-docx暂不支持该操作。此外高版本Word支持解决批注和回复批注功能,但python-docx暂未实现。以下是包含批注创建、富文本批注、元数据修改及批注遍历的完整示例:

python 复制代码
# 导入必要的库
from docx import Document
from datetime import datetime, timezone

"""
创建文档并演示批注的完整操作流程:
1. 创建基础批注
2. 创建富文本格式批注
3. 修改批注元数据
4. 访问和遍历所有批注
"""
# 1. 初始化文档对象
doc = Document()
doc.add_heading("Python-docx 批注演示文档", level=1)

# 2. 示例1:创建基础文本批注
para1 = doc.add_paragraph("这是第一段测试文本,用于添加基础批注。")
# 为整段文本添加基础批注
basic_comment = doc.add_comment(
    runs=para1.runs,  # 关联的文本块
    text="基础批注:这段文本表述清晰,建议保留。",  # 批注纯文本内容
    author="张三",  # 批注作者
    initials="ZS"  # 作者缩写
)
print(f"✅ 基础批注创建完成")
# 3. 示例2:创建富文本格式批注(包含粗体、斜体等样式)
para2 = doc.add_paragraph("这是第二段测试文本,用于添加富文本批注。")
# 创建空内容批注(后续手动添加富文本)
rich_comment = doc.add_comment(
    runs=para2.runs,
    text="",  # 初始为空,后续添加富文本
    author="李四",
    initials="LS"
)
# 向批注中添加富文本内容
cmt_para = rich_comment.paragraphs[0]  # 获取批注的默认段落
cmt_para.add_run("富文本批注:")  # 普通文本
cmt_para.add_run("重点建议:").bold = True  # 粗体文本
cmt_para.add_run("这段文本需要补充案例,").italic = True  # 斜体文本
cmt_para.add_run("建议参考行业标准。")  # 普通文本

# 4. 示例3:修改批注元数据(作者、缩写)
basic_comment.author = "张小三"  # 修改作者名
basic_comment.initials = "ZXS"  # 修改作者缩写
print(f"✅ 基础批注元数据已更新:作者={basic_comment.author},缩写={basic_comment.initials}")

# 5. 示例4:访问和遍历所有批注
print("\n📋 文档中所有批注信息:")
for idx, comment in enumerate(doc.comments):
    print(f"\n批注 {idx+1}:")
    print(f"  作者:{comment.author}")
    print(f"  缩写:{comment.initials}")
    # 提取批注的完整文本(兼容富文本)
    comment_text = "\n  ".join([para.text for para in comment.paragraphs])
    print(f"  内容:{comment_text}")

# 6. 保存文档
doc.save("批注演示文档.docx")
print("\n📄 文档已保存为:批注演示文档.docx")

1.3.3 图片对象

Word文档中的内容分为两个图层:文本层和绘图层。文本层按从左到右、自上而下的顺序排列,页满则自动换页。绘图层中的对象称为形状,可自由放置。图片是一种特殊形状,可置于文本层或绘图层。位于文本层的图片称为嵌入式图片,它在排版中被视为一个大型文本字符:行高会自动调整以容纳图片,并支持类似文本的换行效果。若在其前方插入文本,图片会随之向后移动。嵌入式图片通常独占一个段落,但也可与前后文本共存于同一段落中。

当前python-docx库仅支持嵌入式图片。通过Document.add_picture()方法可将图片默认添加至文档末尾的独立段落中,但经过适当设置,也能实现灵活的图文混排效果,示例如下:

python 复制代码
from docx import Document
from docx.shared import Inches, Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH

# 1. 创建一个新的Word文档
doc = Document()

# 2. 基础用法:在文档末尾插入图片(单独占一个段落)
doc.add_heading('1. 基础插入图片', level=2)
# 添加图片,设置宽度为2英寸(高度会按比例自动调整)
doc.add_picture('test.png', width=Inches(2))

# 3. 进阶用法:图文混排(图片和文本在同一个段落)
doc.add_heading('2. 图文混排示例', level=2)
# 创建一个新段落
mixed_paragraph = doc.add_paragraph()
# 添加文本到段落开头
mixed_paragraph.add_run('这是图片左侧的文本,')
# 在段落中插入图片(非单独段落)
img_run = mixed_paragraph.add_run()
img_run.add_picture('test.png', width=Inches(1))
# 在图片右侧添加文本
mixed_paragraph.add_run('这是图片右侧的文本。')

# 4. 自定义图片尺寸+段落对齐
doc.add_heading('3. 自定义尺寸与对齐', level=2)
align_para = doc.add_paragraph()
align_para.alignment = WD_ALIGN_PARAGRAPH.CENTER  # 段落居中对齐
align_run = align_para.add_run()
# 同时设置宽度和高度(注意:可能导致图片变形)
align_run.add_picture('test.png', width=Inches(1.5), height=Inches(1))
align_para.add_run('\n这是居中显示的图片')

# 5. 保存文档
doc.save('形状图片示例.docx')
print("文档已保存为形状图片示例.docx")

1.4 综合示例

在实际应用中,python-docx主要被用于文档修改和自动化生成,而非从头创建文档。本节将通过一个综合示例,展示如何对一个包含标题、段落、多种表格、多张图片和分页符的多页文档进行针对性修改,并提供相应的文档生成代码。

文档生成

以下代码生成一个示例多页文档,涵盖了日常办公中常见的文档结构:

python 复制代码
from docx import Document
from docx.shared import Inches, Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import qn
import os

def ensure_test_images():
    # 简单生成测试图片(如果没有的话)
    try:
        from PIL import Image
        for i in range(1, 3):
            img_path = f"image{i}.jpg"
            if not os.path.exists(img_path):
                img = Image.new('RGB', (800, 600), color='lightblue')
                img.save(img_path)
        print("测试图片已准备完成")
    except ImportError:
        print("请安装pillow库:pip install pillow")
        exit(1)

# 生成Word文档
def generate_complex_doc():
    # 创建新文档
    doc = Document()
    
    # 设置文档默认字体(解决中文显示问题)
    doc.styles['Normal'].font.name = '微软雅黑'
    doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
    
    # ========== 第一页内容 ==========
    # 标题
    title = doc.add_heading('XX项目调研报告', 0)
    title.alignment = WD_ALIGN_PARAGRAPH.CENTER  # 居中对齐
    title_run = title.runs[0]
    title_run.font.size = Pt(24)
    title_run.font.bold = True
    
    # 副标题/说明
    sub_para = doc.add_paragraph('报告生成时间:2026年1月  报告编制人:测试用户')
    sub_para.alignment = WD_ALIGN_PARAGRAPH.RIGHT
    sub_para_run = sub_para.runs[0]
    sub_para_run.font.size = Pt(10)
    
    # 正文段落
    doc.add_heading('一、项目概述', level=1)
    overview_para = doc.add_paragraph()
    overview_para.add_run('本项目针对企业数字化转型需求,重点解决现有业务系统数据孤岛、流程不规范、决策效率低等核心问题。').font.size = Pt(12)
    overview_para.add_run('\n项目覆盖范围包括:').font.bold = True
    overview_para.add_run('\n1) 生产管理模块:实现生产流程自动化监控')
    overview_para.add_run('\n2) 销售管理模块:打通客户数据与订单系统')
    overview_para.add_run('\n3) 财务管理模块:实现财务数据实时汇总分析')
    
    # 第一个表格:项目基础信息表(2行4列)
    doc.add_heading('1.1 项目基础信息', level=2)
    table1 = doc.add_table(rows=2, cols=4)
    table1.alignment = WD_TABLE_ALIGNMENT.CENTER  # 表格居中
    table1.style = 'Table Grid'  # 带边框样式
    
    # 填充表格1表头
    hdr_cells = table1.rows[0].cells
    hdr_cells[0].text = '项目编号'
    hdr_cells[1].text = '项目名称'
    hdr_cells[2].text = '启动时间'
    hdr_cells[3].text = '预计工期'
    
    # 填充表格1内容
    row_cells = table1.rows[1].cells
    row_cells[0].text = 'XM2026001'
    row_cells[1].text = '企业数字化转型项目'
    row_cells[2].text = '2026-01-01'
    row_cells[3].text = '12个月'
    
    # 调整表格1单元格字体
    for row in table1.rows:
        for cell in row.cells:
            for paragraph in cell.paragraphs:
                for run in paragraph.runs:
                    run.font.size = Pt(10)
                    run.font.name = '微软雅黑'
                    run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
    
    # 插入第一张图片
    doc.add_heading('1.2 项目架构图', level=2)
    img1_para = doc.add_paragraph()
    img1_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
    img1_run = img1_para.add_run()
    img1_run.add_picture('image1.jpg', width=Inches(5))  # 图片宽度5英寸
    img1_para.add_run('\n图1:项目整体架构图').font.size = Pt(10)
    
    # 插入分页符(第一页结束)
    doc.add_page_break()
    
    # ========== 第二页内容 ==========
    doc.add_heading('二、项目资源配置', level=1)
    resource_para = doc.add_paragraph('本项目所需资源包括人力资源、硬件资源、软件资源三大类,具体配置如下表所示:')
    
    # 第二个表格:资源配置表(5行3列)
    table2 = doc.add_table(rows=5, cols=3)
    table2.style = 'Table Grid'
    table2.alignment = WD_TABLE_ALIGNMENT.CENTER
    
    # 填充表格2
    table2_data = [
        ['资源类型', '规格/数量', '备注'],
        ['人力资源', '项目经理1人,开发工程师5人,测试工程师2人', '均为全职'],
        ['服务器', '8核16G云服务器3台', '阿里云ECS'],
        ['数据库', 'MySQL 8.0 主从架构', '数据实时备份'],
        ['软件授权', 'Python、Office、开发工具', '企业版授权']
    ]
    
    for i, row_data in enumerate(table2_data):
        row_cells = table2.rows[i].cells
        for j, cell_text in enumerate(row_data):
            row_cells[j].text = cell_text
            # 设置单元格字体
            for paragraph in row_cells[j].paragraphs:
                for run in paragraph.runs:
                    run.font.size = Pt(10)
                    run.font.name = '微软雅黑'
                    run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
        # 表头加粗
        if i == 0:
            for cell in table2.rows[i].cells:
                for paragraph in cell.paragraphs:
                    for run in paragraph.runs:
                        run.font.bold = True
    
    # 插入第二张图片
    doc.add_heading('2.1 资源部署示意图', level=2)
    img2_para = doc.add_paragraph()
    img2_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
    img2_run = img2_para.add_run()
    img2_run.add_picture('image2.jpg', width=Inches(5))
    img2_para.add_run('\n图2:资源部署拓扑图').font.size = Pt(10)
    
    # 补充段落内容
    doc.add_paragraph('资源配置遵循「够用且预留扩展」原则,硬件资源可根据项目进度弹性扩容,人力资源采用「核心+外协」模式保障交付进度。')
    
    # 插入分页符(第二页结束)
    doc.add_page_break()
    
    # ========== 第三页内容 ==========
    doc.add_heading('三、项目风险评估', level=1)
    risk_para = doc.add_paragraph('通过对技术、人员、成本、进度四个维度的评估,识别出以下核心风险点及应对措施:')
    
    # 第三个表格:风险评估表(4行4列)
    table3 = doc.add_table(rows=4, cols=4)
    table3.style = 'Table Grid'
    table3.alignment = WD_TABLE_ALIGNMENT.CENTER
    
    table3_data = [
        ['风险类型', '风险等级', '影响范围', '应对措施'],
        ['技术风险', '中', '核心功能模块', '提前进行技术预研,制定备选方案'],
        ['进度风险', '低', '整体交付', '设置里程碑节点,每周进度复盘'],
        ['成本风险', '低', '预算管控', '建立成本台账,超支提前预警']
    ]
    
    for i, row_data in enumerate(table3_data):
        row_cells = table3.rows[i].cells
        for j, cell_text in enumerate(row_data):
            row_cells[j].text = cell_text
            for paragraph in row_cells[j].paragraphs:
                for run in paragraph.runs:
                    run.font.size = Pt(10)
                    run.font.name = '微软雅黑'
                    run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
        if i == 0:
            for cell in table3.rows[i].cells:
                for paragraph in cell.paragraphs:
                    for run in paragraph.runs:
                        run.font.bold = True
    
    # 结尾段落
    doc.add_paragraph('\n综上所述,本项目整体可控,通过科学的资源配置和风险管控措施,能够保障项目按计划交付并达到预期目标。')
    
    # 保存文档
    doc.save('项目报告.docx')
    print("项目报告文档已生成")

if __name__ == '__main__':
    ensure_test_images()  # 准备测试图片
    generate_complex_doc()

文档修改

文档修改代码针对上述生成的文档,实现以下常见修改需求:

  • 修改标题和段落文本
  • 修改表格中的内容
  • 替换图片
  • 添加新的段落和表格
  • 修改字体样式

示例代码如下:

python 复制代码
from docx import Document
from docx.shared import Inches, Pt
from docx.shared import RGBColor
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
import os

# 修改已生成的Word文档
def modify_complex_doc(file_path):
    # 打开已生成的文档
    doc = Document(file_path)
    print("已打开待修改的文档")
    
    # ========== 1. 修改标题 ==========
    # 第一个标题是级别0的标题(主标题)
    main_title = doc.paragraphs[0]
    main_title.runs[0].text = 'XX企业数字化转型项目终版报告'  # 修改主标题文本
    main_title.runs[0].font.color.rgb = RGBColor(0, 0, 255)  # 标题改为蓝色
    main_title.runs[0].font.size = Pt(20)
    main_title.runs[0].font.name = '微软雅黑'
    main_title.runs[0]._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
    main_title.alignment = WD_ALIGN_PARAGRAPH.CENTER
    
    # ========== 2. 修改段落文本 ==========
    # 找到"项目概述"下的段落(可通过文本特征定位)
    for para in doc.paragraphs:
        if '本项目针对企业数字化转型需求' in para.text:
            # 清空原有内容,重新写入
            para.clear()
            new_run = para.add_run('本项目针对大型制造企业数字化转型需求,重点解决现有业务系统数据孤岛、流程不规范、决策效率低等核心问题。')
            new_run.font.size = Pt(12)
            new_run.font.name = '微软雅黑'
            new_run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
            # 追加内容
            para.add_run('\n项目升级后新增:')
            para.add_run('\n4) 供应链管理模块:打通上下游供应商数据')
            break
    
    # ========== 3. 修改表格内容并添加表格名 ==========
    # 表格1:项目基础信息表(第一个表格)
    table1 = doc.tables[0]
    # 修改项目编号
    table1.rows[1].cells[0].text = 'XM2026001-FINAL'
    # 修改预计工期
    table1.rows[1].cells[3].text = '10个月'
    # 给修改后的单元格文字标红
    for cell in [table1.rows[1].cells[0], table1.rows[1].cells[3]]:
        for para in cell.paragraphs:
            for run in para.runs:
                run.font.color.rgb = RGBColor(255, 0, 0)
                run.font.size = Pt(11)
                run.font.name = '微软雅黑'
                run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
    
    # 为表格1添加表格名(表题)
    # 找到表格1的位置,在表格上方插入表名
    table1_elem = table1._element
    table_caption1 = doc.add_paragraph('表1 项目基础信息表')
    table_caption1.alignment = WD_ALIGN_PARAGRAPH.CENTER  # 居中对齐
    table_caption1.paragraph_format.space_before = Pt(0)
    table_caption1.paragraph_format.space_after = Pt(0)
    # 设置表名字体格式
    for run in table_caption1.runs:
        run.font.size = Pt(11)
        run.font.name = '微软雅黑'
        run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
        run.font.bold = True  # 加粗
    # 将表名插入到表格上方
    table1_elem.addprevious(table_caption1._element)
    
    # 表格2:资源配置表(第二个表格)
    table2 = doc.tables[1]
    # 修改服务器配置
    table2.rows[3].cells[1].text = '16核32G云服务器3台(升级配置)'
    # 设置表格2单元格字体
    for row in table2.rows:
        for cell in row.cells:
            for para in cell.paragraphs:
                for run in para.runs:
                    run.font.size = Pt(11)
                    run.font.name = '微软雅黑'
                    run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
    
    # 为表格2添加表格名(表题)
    table2_elem = table2._element
    table_caption2 = doc.add_paragraph('表2 项目资源配置表')
    table_caption2.alignment = WD_ALIGN_PARAGRAPH.CENTER
    for run in table_caption2.runs:
        run.font.size = Pt(11)
        run.font.name = '微软雅黑'
        run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
        run.font.bold = True
    table2_elem.addprevious(table_caption2._element)
    
    # ========== 4. 替换图片并规范图名 ==========
    # 先准备一张新的测试图片
    from PIL import Image
    new_img_path = 'test.png'
    if not os.path.exists(new_img_path):
        img = Image.new('RGB', (800, 600), color='lightgreen')
        img.save(new_img_path)
    
    # 找到第一张图片所在的段落(通过图片相关特征)
    img_para_index = -1
    for i, para in enumerate(doc.paragraphs):
        if '图1:项目整体架构图' in para.text or 'graphicData' in str(para._element.xml):
            img_para_index = i
            break
    
    if img_para_index != -1:
        # 清空段落中的图片和文字,重新添加
        img_para = doc.paragraphs[img_para_index]
        img_para.clear()
        
        # 重新添加新图片
        img_run = img_para.add_run()
        img_run.add_picture(new_img_path, width=Inches(5))
        
        # 添加规范的图名(图题)
        img_caption = img_para.add_run('\n图1 项目整体架构图(终版)')
        img_caption.font.size = Pt(10)
        img_caption.font.name = '微软雅黑'
        img_caption._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
        img_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
    
    # ========== 5. 添加新内容并为新表格加表名 ==========
    # 在第三页风险评估后添加新的段落和表格
    # 先找到风险评估标题的位置,在其后添加内容
    for i, para in enumerate(doc.paragraphs):
        if '三、项目风险评估' in para.text:
            # 添加新的子标题
            new_sub_heading = doc.add_heading('3.1 风险应对预案', level=2)
            # 插入到指定位置
            doc.paragraphs.insert(i+1, new_sub_heading)
            
            # 添加新段落
            new_para = doc.add_paragraph('针对高优先级风险,制定专项应对预案,包括应急响应流程、备用资源调配方案等,确保风险发生时可快速处置。')
            new_para.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
            for run in new_para.runs:
                run.font.size = Pt(12)
                run.font.name = '微软雅黑'
                run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
            doc.paragraphs.insert(i+2, new_para)
            
            # 添加新表格(应急预案表)
            new_table = doc.add_table(rows=2, cols=2)
            new_table.style = 'Table Grid'
            new_table.alignment = WD_TABLE_ALIGNMENT.CENTER
            # 填充新表格
            new_table.rows[0].cells[0].text = '风险类型'
            new_table.rows[0].cells[1].text = '应急联系人'
            new_table.rows[1].cells[0].text = '技术风险'
            new_table.rows[1].cells[1].text = '张工 13800138000'
            # 设置新表格字体
            for row in new_table.rows:
                for cell in row.cells:
                    for para_cell in cell.paragraphs:
                        for run in para_cell.runs:
                            run.font.size = Pt(10)
                            run.font.name = '微软雅黑'
                            run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
            
            # 为新表格添加表名
            new_table_caption = doc.add_paragraph('表3 风险应急联系人表')
            new_table_caption.alignment = WD_ALIGN_PARAGRAPH.CENTER
            for run in new_table_caption.runs:
                run.font.size = Pt(11)
                run.font.name = '微软雅黑'
                run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
                run.font.bold = True
            
            # 插入新表格名和新表格
            doc.element.body.insert(i+3, new_table_caption._element)
            doc.element.body.insert(i+4, new_table._element)
            break
    
    # ========== 保存修改后的文档 ==========
    doc.save('项目报告_修改版.docx')
    print("修改后的文档已保存:项目报告_修改版.docx")

if __name__ == '__main__':
    # 确保原文档存在
    file_path = '项目报告.docx'
    if not os.path.exists(file_path):
        print("请先运行生成代码创建原始文档!")
    else:
        modify_complex_doc(file_path)

2 参考