深入解析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
Table Grid
:这个就是标准的边框线全都显现的表格
3. 表格尺寸的设置
- 宽度自适应:
table.autofit = True
- 设置一行的行高:
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()
入参:
- 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. 合同模板化处理
预设条款库,根据业务需求组合生成标准化合同。
七、注意事项与扩展
- 兼容性:部分复杂格式(如VBA宏)需结合
pywin32
库操作原生Word。 - 性能优化:处理超大型文档时,建议分块读写。
- 生态整合:
• docxtpl:基于模板引擎动态渲染内容
• pandas:结合数据分析生成图表报告
八、常见问题及其解决方案
KeyError: "no style with name 'Table Grid'"
:这个是在add_table()
参数里设置style="Table Grid"
时出现的,这个是由于Word版本不同导致的,解决方案是在比较新的Word版本里在表设计这里把第一个表格样式(Table Grid就是这个)设为默认值,重新渲染就可以了:
九、总结
Python-docx以其简洁的API和强大的功能,成为办公自动化领域的核心工具。通过本文的学习,读者可实现从简单文本操作到复杂文档生成的跨越。立即动手实践,让代码解放你的双手!
本文撰写过程中参考的其他网络资料
- Python读写word文档(.docx) python-docx的使用_python 读取docx-CSDN博客
- Python-docx库-常用操作篇-CSDN博客
- Python中的文档处理神器:深度解析python-docx库-CSDN博客
- 【笔记】Python-docx写文档时逐字符设置字体与上下标_python word 上标-CSDN博客
- python table 怎么设置字号 python设置word表格字体_kekenai的技术博客_51CTO博客
- 关于python docx包中,如何对Word自身表格实现复制,并且粘贴到原docx文档中?(已解决) | Python | Python 技术论坛
- ms word - In python-docx how do I delete a table row? - Stack Overflow
- KeyError: u"no style with name ‘Table Grid’"; python 无法创建word表格_keyerror: "no style with name 'table grid-CSDN博客