深入解析Python-docx库:轻松玩转Word文档自动化

诸神缄默不语-个人技术博文与视频目录

文章目录

  • 一、引言
  • 二、安装与环境配置
  • 三、基础操作
    • 1. 创建文档
      • 1. 创建文档对象
      • 2. 标题与段落
      • 3. 保存文档
    • 2. 读取文档
      • 1. 打开文档的三种写法
  • 四、进阶功能:玩转复杂结构
    • 1. 表格操作
      • 1. 对指定单元格设置边框线
      • 2. table.style
      • 3. 表格尺寸的设置
      • 4. 表格级别的字体设置
      • 5. 表格位置设置
      • 6. 设置表格中的文字垂直、水平对齐
      • 7. 复制表格
      • 8. 增删行
    • 2. 图片
    • 3. 分页符
    • 4. 字体和段落样式
      • 1. run级别设置字体格式
      • 2. document级别设置字体格式
      • 3. paragraph级别
        • 1. 设置段落格式
        • 2. 设置字体格式
      • 4. 字号对应关系
      • 5. 自定义段落/字符格式
    • 5. 超链接
    • 6. 分栏
    • 7. 设置页眉页脚
    • 8. 插入文本框
    • 9. 插入目录
    • 10. 插入列表
  • 五、API
    • 1. Document.add_paragraph()
  • 六、实战应用场景
    • 1. 自动化报告生成
    • 2. 简历批量生成
    • 3. 合同模板化处理
  • 七、注意事项与扩展
  • 八、常见问题及其解决方案
  • 九、总结
  • 本文撰写过程中参考的其他网络资料

一、引言

在日常办公中,Word文档处理是高频需求。无论是生成报告、合同,还是批量修改文档内容,手动操作效率低下且易出错。Python-docx作为Python生态中处理.docx文件的王牌库,提供了从文档创建、内容编辑到格式控制的完整解决方案。本文将带您全面掌握该库的核心功能,并附实战代码示例。

注意:仅支持.docx格式(Office 2007及以上版本)。

Document对象相当于一篇文档,由paragraph对象组成。
每个paragraph对象由run对象组成,有一些诸如字体之类的character-level的细节配置需要用run对象来操作。paragraph就是段落,run是一组格式相同的内容,就是如果格式不一样就要切换run了。

python-docx库挺难用的,但是我咋感觉Python上操作Word文档的只有这一个选项呢,也没得选。

二、安装与环境配置

通过pip一键安装:

pip install python-docx

验证安装:

import docx
print(docx.__version__)  # 输出当前版本,如0.8.11

三、基础操作

1. 创建文档

1. 创建文档对象

from docx import Document
doc = Document()  # 新建空白文档

2. 标题与段落

标题:支持1-9级标题(level参数控制层级)

doc.add_heading('Python-docx实战指南', level=0)  # 主标题
doc.add_heading('第一章:基础操作', level=1)  

段落:支持普通文本、换行符及段落样式

para = doc.add_paragraph('这是一个普通段落。')
para.add_run('加粗文本').bold = True  # 追加带格式的文本

更多操作可参考:

# * 向document末尾添加一个段落(返回的是最后的添加的这个段落对象的引用)
paragraph = document.add_paragraph('Lorem ipsum dolor sit amet.')
# * 还可以在这个段落之前添加段落:
prior_paragraph = paragraph.insert_paragraph_before('Lorem ipsum')
# * 下面是如何设置段落的样式
document.add_paragraph('Lorem ipsum dolor sit amet.', style='ListBullet')
# * 或这
paragraph = document.add_paragraph('Lorem ipsum dolor sit amet.')
paragraph.style = 'List Bullet'
# * 上面是block-level的样式(比如缩进)

# * 下面是Character-level的样式(用add_run来设置)
paragraph = document.add_paragraph('Lorem ipsum ')
paragraph.add_run('dolor sit amet.')
paragraph = document.add_paragraph('Lorem ipsum ')
run = paragraph.add_run('dolor')
run.bold = True
paragraph.add_run(' sit amet.')
paragraph.add_run('dolor').bold = True
# * 等同于:
run = paragraph.add_run('dolor')
run.bold = True
paragraph = document.add_paragraph()
paragraph.add_run('Lorem ipsum ')
paragraph.add_run('dolor').bold = True
paragraph.add_run(' sit amet.')

3. 保存文档

doc.save('demo.docx')  # 保存至当前目录

2. 读取文档

from docx import Document

# 打开文档
doc = Document('example.docx')

# 遍历段落
for para in doc.paragraphs:
    print(para.text)

# 遍历表格
for table in doc.tables:
    for row in table.rows:
        for cell in row.cells:
            print(cell.text)

1. 打开文档的三种写法

写法1:

document = Document('existing-document-file.docx')

写法2:

f = open('foobar.docx', 'rb')
document = Document(f)
f.close()

写法3(带保存文档):

with open('foobar.docx', 'rb') as f:
    source_stream = StringIO(f.read())
document = Document(source_stream)
source_stream.close()
target_stream = StringIO()
document.save(target_stream)

四、进阶功能:玩转复杂结构

1. 表格操作

表格是一个table对象,里面每一个单元格是一个cell,里面呈现的内容是一堆paragraph。所以表格里的字体的细微设置也可以通过这些paragraph和里面的run来实现。

自动生成数据表格并设置样式:

table = document.add_table(rows=3, cols=3)  # 创建3x3表格
table.style = 'LightShading-Accent1'  # 应用预定义样式

# 获取第一行第二列的单元格,并修改单元格内容
cell = table.cell(0, 1)
cell.text = 'parrot, possibly dead'

# 获取第一行,并修改单元格内容
row = table.rows[1]
row.cells[0].text = 'Foo bar to you.'
row.cells[1].text = 'And a hearty foo bar to you too sir!'
    
# 获取单元格的行列数
row_count = len(table.rows)
col_count = len(table.columns)
row = table.add_row()

# 添加表格数据
items = (
(7, '1024', 'Plush kittens'),
(3, '2042', 'Furbees'),
(1, '1288', 'French Poodle Collars, Deluxe'),
)
# 下面是添加一个1行3列的表格
table = document.add_table(1, 3)
# 修改表格中第一行的数据
heading_cells = table.rows[0].cells
heading_cells[0].text = 'Qty'
heading_cells[1].text = 'SKU'
heading_cells[2].text = 'Description'

# 一行一行添加数据
for item in items:
    cells = table.add_row().cells
    cells[0].text = str(item[0])
    cells[1].text = item[1]
    cells[2].text = item[2]

# 另一种填充数据方法
table.cell(0, 0).text = '单元格 1,1'
table.cell(0, 1).text = '单元格 1,2'
table.cell(1, 0).text = '单元格 2,1'
table.cell(1, 1).text = '单元格 2,2'

# 表格数据也可以这么遍历和批量填充
for row in table.rows:
    for cell in row.cells:
        cell.text = "数据单元"

# 合并单元格示例
table.cell(0,0).merge(table.cell(1,1))  # 跨行列合并

1. 对指定单元格设置边框线

函数:

from docx.oxml import OxmlElement
from docx.oxml.ns import qn

def set_cell_border(cell, **kwargs):
    """
    Set cell`s border
    Usage:
    set_cell_border(
        cell,
        top={"sz": 12, "val": "single", "color": "#FF0000", "space": "0"},
        bottom={"sz": 12, "color": "#00FF00", "val": "single"},
        left={"sz": 24, "val": "dashed", "shadow": "true"},
        right={"sz": 12, "val": "dashed"},
    )
    """
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()

    # check for tag existnace, if none found, then create one
    tcBorders = tcPr.first_child_found_in("w:tcBorders")
    if tcBorders is None:
        tcBorders = OxmlElement('w:tcBorders')
        tcPr.append(tcBorders)

    # list over all available tags
    for edge in ('left', 'top', 'right', 'bottom', 'insideH', 'insideV'):
        edge_data = kwargs.get(edge)
        if edge_data:
            tag = 'w:{}'.format(edge)

            # check for tag existnace, if none found, then create one
            element = tcBorders.find(qn(tag))
            if element is None:
                element = OxmlElement(tag)
                tcBorders.append(element)

            # looks like order of attributes is important
            for key in ["sz", "val", "color", "space", "shadow"]:
                if key in edge_data:
                    element.set(qn('w:{}'.format(key)), str(edge_data[key]))

使用示例:

table = document.add_table(rows=1, cols=3)
hdr_cells = table.rows[0].cells
hdr_cells[0].text = 'Qty'

set_cell_border(
        hdr_cells[0],
        top={"sz": 4, "val": "single", "color": "#000000", "space": "0"},
        bottom={"sz": 4, "val": "single", "color": "#000000", "space": "0"},
        left={"sz": 4, "val": "single", "color": "#000000", "space": "0"},
        right={"sz": 4, "val": "single", "color": "#000000", "space": "0"},
    )

2. table.style

  1. Table Grid:这个就是标准的边框线全都显现的表格

3. 表格尺寸的设置

  1. 宽度自适应:table.autofit = True
  2. 设置一行的行高:
    from docx.shared import Cm
    
    table.rows[0].height = Cm(0.93)
    

4. 表格级别的字体设置

from docx.shared import Pt

table.style.font.size = Pt(12)
table.style.font.name = "Times New Roman"

(经测试好像中文字体设置了也没用,所以我的个人建议是要么通过run来单独设置,要么直接设置全document的字体,见本节第四小节“字体和段落样式”第二小节“document级别设置字体”)

5. 表格位置设置

居中:

from docx.enum.table import WD_TABLE_ALIGNMENT

table.alignment = WD_TABLE_ALIGNMENT.CENTER`

6. 设置表格中的文字垂直、水平对齐

这个我只找到逐cell来设置的代码:

from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_ALIGN_VERTICAL

cell.paragraphs[0].paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER

7. 复制表格

以复制第一个表格为例:

from copy import deepcopy

table = deepcopy(doc.tables[0])
para1 = doc.add_paragraph()
para1._p.addnext(table._element)

8. 增删行

增加行:

table.add_row()

删除行(以删除最后一行为例):

def remove_row(table, row):
    tbl = table._tbl
    tr = row._tr
    tbl.remove(tr)

row = table.rows[len(table.rows) - 1]
remove_row(table, row)

2. 图片

支持调整尺寸(单位英寸或厘米)

from docx.shared import Inches
doc.add_picture('logo.png', width=Inches(2.5), height=Inches(2))  # 指定宽高  

3. 分页符

控制文档结构

写法一:

doc.add_page_break()  # 强制分页

写法二:

doc.paragraphs[-1].add_run().add_break(WD_BREAK.PAGE)

4. 字体和段落样式

1. run级别设置字体格式

颜色、大小、加粗、斜体、下划线、上标下标

from docx.shared import RGBColor, Pt
run = para.add_run('红色斜体文本')
run.font.color.rgb = RGBColor(255,0,0)  # 设置红色
run.font.size = Pt(14)  # 字号14磅
run.bold = True  #加粗
run.italic = True  # 斜体
run.underline = True

run2=para.add_run("1")
run2.font.subscript = True  # 下标

run3=para.add_run("2")
run3.font.superscript = True  # 上标

2. document级别设置字体格式

大小、字体

from docx.shared import Pt
from docx.oxml.ns import qn

document.styles["Normal"].font.size = Pt(12)
document.styles['Normal'].font.name = 'Times New Roman'
document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')

3. paragraph级别

1. 设置段落格式

缩进、间距、对齐

from docx.enum.text import WD_ALIGN_PARAGRAPH

para_format = para.paragraph_format
para_format.alignment = WD_ALIGN_PARAGRAPH.CENTER  # 居中对齐
para_format.line_spacing = Pt(18)  # 行间距
2. 设置字体格式

字号

from docx.shared import Pt

para1.style.font.size = Pt(12)

4. 字号对应关系

小四 - Pt(12)

5. 自定义段落/字符格式

自定义格式:

# 创建自定义段落样式(第一个参数为样式名, 第二个参数为样式类型, 1为段落样式, 2为字符样式, 3为表格样式)
UserStyle1 = document.styles.add_style('UserStyle1', 1)
# 设置字体尺寸
UserStyle1.font.size = Pt(40)
# 设置字体颜色
UserStyle1.font.color.rgb = RGBColor(0xff, 0xde, 0x00)
# 居中文本
UserStyle1.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
# 设置中文字体
UserStyle1.font.name = '微软雅黑'
UserStyle1._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')

使用格式:

# 使用自定义段落样式
document.add_paragraph('自定义段落样式', style = UserStyle1)

# 使用自定义字符样式
document.add_paragraph('').add_run('正月里采花无哟花采', style = UserStyle2)

5. 超链接

from docx.oxml import OxmlElement
from docx.oxml.ns import qn

def add_hyperlink(paragraph, url, text):
    # 创建超链接元素
    part = paragraph.part
    r_id = part.relate_to(url, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", is_external=True)
    hyperlink = OxmlElement('w:hyperlink')
    hyperlink.set(qn('r:id'), r_id)
    
    # 创建文字元素
    run = OxmlElement('w:r')
    run_text = OxmlElement('w:t')
    run_text.text = text
    run.append(run_text)
    hyperlink.append(run)
    
    # 添加到段落中
    paragraph._p.append(hyperlink)

doc = Document()
p = doc.add_paragraph('点击访问: ')
add_hyperlink(p, 'https://www.example.com', '示例链接')

doc.save('hyperlink.docx')

6. 分栏

from docx import Document
from docx.oxml import OxmlElement
from docx.oxml.ns import qn

document = Document()
section = document.sections[0]
# * 设置栏目数为2
sectPr = section._sectPr
cols = sectPr.xpath('./w:cols')[0]
cols.set(qn('w:num'),'2')

document.save('demo.docx')

# * 下面是对每个栏目进行格式设置
from docx import Document from docx.shared import Cm, Pt
document = Document()
sections = document.sections
for section in sections: 
      section.start_type = 1 # 设置起始页为奇数 
      section.orientation = 0 # 设置页面方向为纵向 
      section.page_width = Cm(21) # 设置页面宽度 
      section.page_height = Cm(29.7) # 设置页面高度 
      section.left_margin = Cm(2) # 设置左边距 
      section.right_margin = Cm(2) # 设置右边距 
      section.top_margin = Cm(2) # 设置上边距 
      section.bottom_margin = Cm(2) # 设置下边距 
      section.gutter = Cm(0.5) # 设置分栏之间的间隔 
      section.cols_num = 2 # 设置分栏数

7. 设置页眉页脚

# ****************设置普通页眉*********
doc = Document('existing-document-file.docx')
doc.sections[0].header.paragraphs[0].text = "这是第1节页眉"
doc.save('existing-document-file.docx')
# ****************设置奇偶页眉*********
doc = Document('existing-document-file.docx')
doc.settings.odd_and_even_pages_header_footer = True
doc.sections[0].even_page_header.paragraphs[0].text = "这是偶数页页眉"
doc.sections[0].header.paragraphs[0].text = "这是奇数页页眉"
doc.save('existing-document-file.docx')
# ****************设置奇偶页眉*********
doc = Document('existing-document-file.docx')
doc.sections[0].different_first_page_header_footer = True
doc.sections[0].first_page_header.paragraphs[0].text = "这是首页页眉"
doc.save('existing-document-file.docx')

8. 插入文本框

from docx import Document

doc = Document()
from docx.shared import Inches

width = Inches(1.0)  # 设置文本框宽度
height = Inches(4.0)  # 设置文本框高度

# 插入文本框
text_box = doc.add_textbox(width, height)
from docx.shared import Pt
from docx.enum.text import WD_ALIGN_VERTICAL

# 在文本框中添加一个段落
paragraph = text_box.add_paragraph()

# 设置文本方向为竖排
paragraph.alignment = WD_ALIGN_VERTICAL.ORIENT_90

# 添加竖排文本
text = "竖排文本"
run = paragraph.add_run(text)

# 设置文本样式,例如字体大小和颜色
font = run.font
font.size = Pt(12)  # 设置字体大小
font.color.rgb = (0x00, 0x00, 0x00)  # 设置字体颜色(这里是黑色)
doc.save("vertical_text.docx")

9. 插入目录

from docx.oxml.ns import qn
from docx.oxml import OxmlElement

paragraph = self.document.add_paragraph()
run = paragraph.add_run()
fldChar = OxmlElement('w:fldChar')  # creates a new element
fldChar.set(qn('w:fldCharType'), 'begin')  # sets attribute on element
instrText = OxmlElement('w:instrText')
instrText.set(qn('xml:space'), 'preserve')  # sets attribute on element
instrText.text = 'TOC \o "1-3" \h \z \u'   # change 1-3 depending on heading levels you need

fldChar2 = OxmlElement('w:fldChar')
fldChar2.set(qn('w:fldCharType'), 'separate')
fldChar3 = OxmlElement('w:t')
fldChar3.text = "Right-click to update field."
fldChar2.append(fldChar3)

fldChar4 = OxmlElement('w:fldChar')
fldChar4.set(qn('w:fldCharType'), 'end')

r_element = run._r
r_element.append(fldChar)
r_element.append(instrText)
r_element.append(fldChar2)
r_element.append(fldChar4)
p_element = paragraph._p

# 下面是自动更新目录
import lxml
import os

from docx import Document

# 设置待自动更新目录的文件
file_name = "test.docx"

# 读取文件,初始化为document对象
word_obj = Document(os.path.realpath(file_name))

# 初始化各项参数
name_space = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}"
update_name_space = "%supdateFields" % name_space
val_name_space = "%sval" % name_space

# 自动更新目录
try:
    element_update_field_obj = lxml.etree.SubElement(word_obj.settings.element, update_name_space)
    element_update_field_obj.set(val_name_space, "true")
except Exception as e:
    del e

# 保存更新后的word文件对象
word_obj.save(os.path.realpath(file_name))

10. 插入列表

有序列表:

document.add_paragraph('把冰箱门打开', style='List Number')
document.add_paragraph('把大象装进去', style='List Number')
document.add_paragraph('把冰箱门关上', style='List Number')

无序列表:

document.add_paragraph('天地匆匆', style='List Bullet')
document.add_paragraph('遑遑无归', style='List Bullet')
document.add_paragraph('引势而流', style='List Bullet')
document.add_paragraph('乾震坎艮', style='List Bullet')

五、API

1. Document.add_paragraph()

入参:

  1. style:如'Heading 1'

六、实战应用场景

1. 自动化报告生成

结合数据源(如Excel/Pandas),动态填充表格和图表,生成周报/月报。

2. 简历批量生成

通过模板替换变量(姓名、教育背景等),快速生成千人级个性化简历。

def create_resume(data):
    doc = Document()
    doc.add_heading(data['name'], level=1)
    # 动态填充教育、工作经验等模块...
    return doc.save(f"{data['name']}_简历.docx")

3. 合同模板化处理

预设条款库,根据业务需求组合生成标准化合同。

七、注意事项与扩展

  1. 兼容性:部分复杂格式(如VBA宏)需结合pywin32库操作原生Word。
  2. 性能优化:处理超大型文档时,建议分块读写。
  3. 生态整合
    docxtpl:基于模板引擎动态渲染内容
    pandas:结合数据分析生成图表报告

八、常见问题及其解决方案

  1. KeyError: "no style with name 'Table Grid'":这个是在add_table()参数里设置style="Table Grid"时出现的,这个是由于Word版本不同导致的,解决方案是在比较新的Word版本里在表设计这里把第一个表格样式(Table Grid就是这个)设为默认值,重新渲染就可以了:
    深入解析Python-docx库:轻松玩转Word文档自动化_第1张图片

九、总结

Python-docx以其简洁的API和强大的功能,成为办公自动化领域的核心工具。通过本文的学习,读者可实现从简单文本操作到复杂文档生成的跨越。立即动手实践,让代码解放你的双手!

本文撰写过程中参考的其他网络资料

  1. Python读写word文档(.docx) python-docx的使用_python 读取docx-CSDN博客
  2. Python-docx库-常用操作篇-CSDN博客
  3. Python中的文档处理神器:深度解析python-docx库-CSDN博客
  4. 【笔记】Python-docx写文档时逐字符设置字体与上下标_python word 上标-CSDN博客
  5. python table 怎么设置字号 python设置word表格字体_kekenai的技术博客_51CTO博客
  6. 关于python docx包中,如何对Word自身表格实现复制,并且粘贴到原docx文档中?(已解决) | Python | Python 技术论坛
  7. ms word - In python-docx how do I delete a table row? - Stack Overflow
  8. KeyError: u"no style with name ‘Table Grid’"; python 无法创建word表格_keyerror: "no style with name 'table grid-CSDN博客

深入解析Python-docx库:轻松玩转Word文档自动化_第2张图片

你可能感兴趣的:(编程学习笔记,python,word,自动化)