使用 Python 实现 Word 文档文本格式化全解析

0 阅读5分钟

周一早上九点,你打开电脑,发现领导昨晚十一点发来的消息:“小张,帮我把这份合同里所有的公司名称改成加粗红色,五十页那个,今天开会要用。”

你打开文档,翻到第一页,选中公司名,点加粗,点红色,然后滚到下一页。五十页的文档,光滚动就花了五分钟。你揉了揉眼睛,心想这要是每个月都来这么一次,眼睛迟早要废。

这种机械化的Word排版工作,最适合交给Python。python-docx这个库,就是专门干这个的。


先搞清楚Word文档的“三层结构”

在用python-docx之前,得先理解一个概念:Word文档的文本不是一整块,而是分了三层。

最外层是Document,代表整个文档。往下一层是Paragraph,也就是段落。再往下是Run,这才是真正存放文字的地方。

为什么要有Run这个概念?因为一个段落里可能有好几种不同的格式。比如“这是加粗的文字”这句话,实际上被分成了三个Run:第一个Run是“这是”,第二个Run是“加粗”(带加粗属性),第三个Run是“的文字”。每个Run可以单独设置字体、大小、颜色。

理解这个结构,后面的所有操作就好办了——想改格式,就要找到对应的Run,而不是直接改段落。


环境准备

先装库:

pip install python-docx

在代码里导入的时候,用docx这个名字,不是python-docx

from docx import Document
from docx.shared import PtRGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH

shared里面是尺寸和颜色相关的工具,enum.text里是对齐方式之类的常量。


读取现有文档

假设你要处理的是那份五十页的合同:

doc = Document('合同.docx')

这样就加载进来了。doc.paragraphs里放着所有段落,你可以遍历它们:

for para in doc.paragraphs:
    print(para.text)

但只打印文本不够,你要改的是格式。格式藏在每个段落的runs里面:

for para in doc.paragraphs:
    for run in para.runs:
        # 在这里操作每个run


改字体:加粗、颜色、大小

回到那个需求:把所有“某某科技有限公司”改成加粗红色。

from docx import Document
from docx.shared import Pt, RGBColor

doc = Document('合同.docx')

target = '某某科技有限公司'

for para in doc.paragraphs:
    for run in para.runs:
        if target in run.text:
            # 把这个run里的目标文字单独拎出来改格式
            # 注意:如果run里不止目标文字,需要拆开处理
            run.font.bold = True
            run.font.color.rgb = RGBColor(25500)  # 红色
            run.font.size = Pt(12)  # 字号,可选

但这里有个坑。如果一个run里是“本合同的乙方为某某科技有限公司,特此声明”,你直接把整个run加粗标红,那整句话都变了。你只想要“某某科技有限公司”这个关键词变色。

解决办法是把run拆开:

for para in doc.paragraphs:
    for run in para.runs:
        if target in run.text:
            # 获取run的原始格式
            original_text = run.text
            # 按目标关键词拆分
            parts = original_text.split(target)
            
            # 清空当前run
            run.text = parts[0]
            
            # 插入目标关键词,带新格式
            new_run = para.add_run(target)
            new_run.font.bold = True
            new_run.font.color.rgb = RGBColor(25500)
            
            # 插入剩下的文本,保留原格式
            if len(parts) > 1:
                remaining_run = para.add_run(parts[1])
                # 复制原格式
                remaining_run.font.size = run.font.size
                remaining_run.font.name = run.font.name

这个逻辑稍微有点绕,但核心思路就是:找到关键词,把run拆成三段,中间那段单独设格式。


改段落:对齐、行距、缩进

有时候你需要改的不是某个词,而是整个段落的样式。比如把某些段落居中对齐。

from docx.enum.text import WD_ALIGN_PARAGRAPH

for para in doc.paragraphs:
    if '重要提示' in para.text:
        para.alignment = WD_ALIGN_PARAGRAPH.CENTER

行距和段间距也可以调。paragraph_format这个属性管这些:

from docx.shared import Pt

for para in doc.paragraphs:
    paragraph_format = para.paragraph_format
    paragraph_format.line_spacing = Pt(20)  # 行距20磅
    paragraph_format.space_before = Pt(12)  # 段前距
    paragraph_format.space_after = Pt(6)    # 段后距

首行缩进是中文文档的常见需求:

from docx.shared import Pt

for para in doc.paragraphs:
    # 排除标题等不需要缩进的段落
    if len(para.text) > 10 and not para.text.startswith('一、'):
        para.paragraph_format.first_line_indent = Pt(24)  # 两个字符


改标题样式

文档里的标题通常有自己的样式。python-docx可以直接通过样式名来设置:

# 添加一个新标题
doc.add_heading('第一章 概述', level=1)

但如果要改已有的标题样式,可以这样:

for para in doc.paragraphs:
    if para.style.name.startswith('Heading'):
        # 这是标题段落
        for run in para.runs:
            run.font.bold = True
            run.font.size = Pt(16)


中文字体的问题

默认情况下,设置run.font.name = '宋体'可能不生效。因为Word里的中文字体需要用另一个属性指定:

from docx.oxml.ns import qn

run = para.add_run('这是一段中文')
run.font.name = '宋体'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')

w:eastAsia就是指定东亚字体的那个属性。这个写法有点绕,但好在可以封装成一个函数:

def set_chinese_font(run, font_name):
    run.font.name = font_name
    run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name)


批量处理多个文档

如果不止一份合同要改,而是整个文件夹的文档都需要统一格式:

import os
from docx import Document

folder = '待处理文档'
output_folder = '处理后文档'

for filename in os.listdir(folder):
    if filename.endswith('.docx'):
        filepath = os.path.join(folder, filename)
        doc = Document(filepath)
        
        # 在这里执行你的格式化操作
        for para in doc.paragraphs:
            for run in para.runs:
                # 你的逻辑
                pass
        
        # 另存,避免覆盖原文件
        output_path = os.path.join(output_folder, filename)
        doc.save(output_path)


保存

改完之后,记得保存:

doc.save('合同_已格式化.docx')

建议另存为新文件,万一改错了还能回去找原版。


回到那个周一早上的场景。如果你用上面的代码跑一遍,五十页的合同大概只需要两秒钟。加粗、标红、调字号、改标题样式,一气呵成。剩下的时间,你可以安心喝杯咖啡,而不是在文档里反复滚动鼠标。

python-docx不能帮你写报告,但至少能让你不用再做那些重复的点鼠标动作。这大概是它最值钱的地方。