Python 办公自动化:Word 文档操作指南
环境安装
使用 Python 操作 Word 文档主要涉及写操作,也有部分读操作。本次教程将讲解以下 4 个库的安装与使用。
介绍使用 Python 进行 Word 文档自动化的方法。涵盖 python-docx、win32com、mailmerge 和 matplotlib 库的安装与使用。内容包括新建与编辑文档、格式转换(doc 转 docx、word 转 pdf)、表格处理、图片插入、图表绘制以及批量生成合同等场景。通过实际案例演示如何读取 Excel 数据并生成包含统计图表的 Word 分析报告,实现办公流程自动化。

使用 Python 操作 Word 文档主要涉及写操作,也有部分读操作。本次教程将讲解以下 4 个库的安装与使用。
python -m pip install -U pip setuptools
大部分操作使用此库。
安装方法:
pip install python-docx
使用方法:
from docx import Document
from docx.shared import Inches
主要用于 doc 转 docx 格式转换及调用本地 Word 应用。
安装方法:
pip install pypiwin32
使用方法:
import win32com
from win32com.client import Dispatch, constants
用于按照模板生成大量同类型文档。
安装方法:
pip install docx-mailmerge
使用方法:
from mailmerge import MailMerge
Python 绘图库,本期简单讲解。
安装方法:
pip install matplotlib
使用方法:
import matplotlib.pyplot as plt
from docx import Document
document = Document()
document.save('new.docx')
from docx import Document
def GenerateNewWord(filename):
document = Document()
document.save(filename)
if __name__ == "__main__":
print("大家好!我们今天开始学习 word 文档自动化")
print("我们先来直接生成一个名为 new.docx 的文档")
document = Document()
document.save('new.docx')
print("没错,里面什么都没有")
print("我们使用函数生成一个 word 文档试试")
newname = '使用函数生成的文档.docx'
GenerateNewWord(newname)
很多时候需要在已存在的 Word 文档上添加内容。
from docx import Document
document = Document('exist.docx')
document.save('new.docx')
这三行代码即可完成旧文档的复制。如需修改,直接在加载后添加内容即可。
利用 win32com 可以调用本地 Word 应用程序进行格式转换。
import os
from win32com import client as wc
def TransDocToDocx(oldDocName, newDocxName):
print("我是 TransDocToDocx 函数")
# 打开 word 应用程序
word = wc.Dispatch('Word.Application')
# 打开 旧 word 文件
doc = word.Documents.Open(oldDocName)
# 保存为 新 word 文件,其中参数 12 表示的是 docx 文件
doc.SaveAs(newDocxName, 12)
# 关闭 word 文档
doc.Close()
word.Quit()
print("生成完毕!")
if __name__ == "__main__":
currentPath = os.getcwd()
print("当前路径为:", currentPath)
docName = os.path.join(currentPath, '旧 doc 格式文档.doc')
print("docFilePath = ", docName)
docxName = os.path.join(currentPath, '新生成 docx 格式文档.docx')
TransDocToDocx(docName, docxName)
import win32com
from win32com.client import Dispatch, constants
import os
def funOpenNewFile():
word = Dispatch('Word.Application')
# 或者使用下面的方法,使用启动独立的进程:
# word = DispatchEx('Word.Application')
# 如果不声明以下属性,运行的时候会显示的打开 word
word.Visible = 1 # 0:后台运行 1:前台运行 (可见)
word.DisplayAlerts = 0 # 不显示,不警告
# 创建新的 word 文档
doc = word.Documents.Add()
# 在文档开头添加内容
myRange1 = doc.Range(0, 0)
myRange1.InsertBefore('Hello word\n')
# 在文档末尾添加内容
myRange2 = doc.Range()
myRange2.InsertAfter('Bye word\n')
# 在文档指定位置添加内容
i = 0
myRange3 = doc.Range(0, i)
myRange3.InsertAfter("what's up, bro?\n")
# doc.Save() # 保存
doc.SaveAs(os.getcwd() + "\\funOpenNewFile.docx") # 另存为
doc.Close() # 关闭 word 文档
word.Quit() # 关闭 office
if __name__ == '__main__':
print("当前文件路径名:", os.getcwd())
print("调用 funOpenNewFile()")
funOpenNewFile()
前提条件:需确保目标文件存在。
import win32com
from win32com.client import Dispatch, constants
import os
def funOpenExistFile():
word = Dispatch('Word.Application')
word.Visible = 1
word.DisplayAlerts = 0
doc = word.Documents.Open(os.getcwd() + "\\3.1 win32com 测试.docx")
myRange1 = doc.Range(0, 0)
myRange1.InsertBefore('Hello word\n')
myRange2 = doc.Range()
myRange2.InsertAfter('Bye word\n')
i = 0
myRange3 = doc.Range(0, i)
myRange3.InsertAfter("what's up, bro?\n")
doc.SaveAs(os.getcwd() + "\\funOpenExistFile.docx")
doc.Close()
word.Quit()
if __name__ == '__main__':
print("当前文件路径名:", os.getcwd())
print("调用 funOpenExistFile()")
funOpenExistFile()
import win32com
from win32com.client import Dispatch, constants
import os
def funGeneratePDF():
word = Dispatch("Word.Application")
word.Visible = 0 # 后台运行,不显示
word.DisplayAlerts = 0 # 不警告
doc = word.Documents.Open(os.getcwd() + "\\3.3 win32com 转换 word 为 pdf 等格式.docx")
doc.SaveAs(os.getcwd() + "\\3.3 win32com 转换 word 为 pdf 等格式.pdf", 17) # txt=4, html=10, docx=16,pdf=17
doc.Close()
word.Quit()
if __name__ == '__main__':
funGeneratePDF()
导入库操作:
from docx import Document
from docx.shared import Inches
新建一个文档:
document = Document()
加载旧文档(用于修改或添加内容):
document = Document('exist.docx')
添加标题段落:
document.add_heading('Document Title', 0)
添加段落操作:
p = document.add_paragraph('A plain paragraph having some ')
p.add_run('bold').bold = True
p.add_run(' and some ')
p.add_run('italic.').italic = True
添加标题操作:
等级 1-9 也就是标题 1-标题 9,可以在旧文档中将标题格式设置好,使用 Python-docx 打开旧文档,再添加相应等级标题即可。
document.add_heading('Heading, level 1', level=1)
添加指定样式段落:
document.add_paragraph('Intense Quote', style='Intense Quote')
# 以下两句的含义等同于上面一句
p = document.add_paragraph('Intense Quote')
p.style = 'Intense Quote'
添加无序列表操作:
document.add_paragraph('first item in unordered list', style='List Bullet')
添加有序列表操作:
document.add_paragraph('first item in ordered list', style='List Number')
添加图片操作:
第一个参数为图片路径,需要正确无误;第二个参数为图片大小,单位英寸。
document.add_picture('countrygarden.png', width=Inches(1.25))
新建表格操作:
table = document.add_table(rows=1, cols=3)
hdr_cells = table.rows[0].cells
hdr_cells[0].text = 'Qty'
hdr_cells[1].text = 'Id'
hdr_cells[2].text = 'Desc'
for qty, id, desc in records:
row_cells = table.add_row().cells
row_cells[0].text = str(qty)
row_cells[1].text = id
row_cells[2].text = desc
设置标题样式操作:
table.style = 'LightShading-Accent1'
添加分页符操作:
document.add_page_break()
保存当前文档操作:
document.save('4.1 Python-docx 官方例程.docx')
遍历所有样式:
from docx.enum.style import WD_STYLE_TYPE
from docx import Document
document = Document()
styles = document.styles
# 生成所有表样式
for s in styles:
if s.type == WD_STYLE_TYPE.TABLE:
document.add_paragraph("表格样式 : " + s.name)
table = document.add_table(3, 3, style=s)
heading_cells = table.rows[0].cells
heading_cells[0].text = '第一列内容'
heading_cells[1].text = '第二列内容'
heading_cells[2].text = '第三列内容'
document.add_paragraph("\n")
document.save('4.3 所有表格样式.docx')
使用 xlrd 作为数据获取所使用的库。
import xlrd
xlsx = xlrd.open_workbook('./3_1 xlrd 读取 操作练习.xlsx')
table = xlsx.sheet_by_index(0)
value = table.cell_value(2, 1)
print("第 3 行 2 列值为", value)
nrows = table.nrows
print("表格一共有", nrows, "行")
name_list = [str(table.cell_value(i, 3)) for i in range(1, nrows)]
print("第 4 列所有的值:", name_list)
只获取用户姓名和分数,并将它们保存到列表中。
def GetExcelInfo():
print("开始获取表格内容信息")
xlsx = xlrd.open_workbook('学生成绩表格.xlsx')
sheet = xlsx.sheet_by_index(0)
nrows = sheet.nrows
print("一共 ", nrows, " 行数据")
nameList = [str(sheet.cell_value(i, 1)) for i in range(1, nrows)]
scoreList = [int(sheet.cell_value(i, 3)) for i in range(1, nrows)]
return nameList, scoreList
将获取的姓名和成绩使用字典数据结构关联起来,再对其排序。
scoreDictionary = dict(zip(nameList, scoreList))
scoreOrder = sorted(scoreDictionary.items(), key=lambda x: x[1], reverse=True)
使用 matplotlib 生成柱状图:
def GenerateScorePic(scoreList):
xNameList = [str(studentInfo[0]) for studentInfo in scoreList]
yScoreList = [int(studentInfo[1]) for studentInfo in scoreList]
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
plt.figure(figsize=(10, 5))
plt.bar(x=xNameList, height=yScoreList, label='学生成绩', color='steelblue', alpha=0.8)
for x1, yy in scoreList:
plt.text(x1, yy + 1, str(yy), ha='center', va='bottom', fontsize=16, rotation=0)
plt.title("学生成绩柱状图")
plt.xlabel("学生姓名")
plt.ylabel("学生成绩")
plt.legend()
plt.xticks(rotation=90)
plt.gcf().subplots_adjust(bottom=0.25)
plt.savefig("studentScore.jpg")
plt.show()
def GenerateScoreReport(scoreOrder, picPath):
document = Document()
document.add_heading('数据分析报告', 0)
p1 = document.add_paragraph("分数排在第一的学生姓名为:")
p1.add_run(scoreOrder[0][0]).bold = True
p1.add_run(" 分数为:")
p1.add_run(str(scoreOrder[0][1])).italic = True
p2 = document.add_paragraph("共有:")
p2.add_run(str(len(scoreOrder))).bold = True
p2.add_run(" 名学生参加了考试,学生考试的总体情况:")
table = document.add_table(rows=1, cols=2)
table.style = 'Medium Grid 1 Accent 1'
hdr_cells = table.rows[0].cells
hdr_cells[0].text = '学生姓名'
hdr_cells[1].text = '学生分数'
for studentName, studentScore in scoreOrder:
row_cells = table.add_row().cells
row_cells[0].text = studentName
row_cells[1].text = str(studentScore)
document.add_picture(picPath, width=Inches(6))
document.save('学生成绩报告.docx')
import xlrd
import matplotlib
import matplotlib.pyplot as plt
from docx import Document
from docx.shared import Inches
def GetExcelInfo():
print("开始获取表格内容信息")
xlsx = xlrd.open_workbook('学生成绩表格.xlsx')
sheet = xlsx.sheet_by_index(0)
nrows = sheet.nrows
print("一共 ", nrows, " 行数据")
nameList = [str(sheet.cell_value(i, 1)) for i in range(1, nrows)]
scoreList = [int(sheet.cell_value(i, 3)) for i in range(1, nrows)]
return nameList, scoreList
def GenerateScorePic(scoreList):
xNameList = [str(studentInfo[0]) for studentInfo in scoreList]
yScoreList = [int(studentInfo[1]) for studentInfo in scoreList]
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
plt.figure(figsize=(10, 5))
plt.bar(x=xNameList, height=yScoreList, label='学生成绩', color='steelblue', alpha=0.8)
for x1, yy in scoreList:
plt.text(x1, yy + 1, str(yy), ha='center', va='bottom', fontsize=16, rotation=0)
plt.title("学生成绩柱状图")
plt.xlabel("学生姓名")
plt.ylabel("学生成绩")
plt.legend()
plt.xticks(rotation=90)
plt.gcf().subplots_adjust(bottom=0.25)
plt.savefig("studentScore.jpg")
plt.show()
def GenerateScoreReport(scoreOrder, picPath):
document = Document()
document.add_heading('数据分析报告', 0)
p1 = document.add_paragraph("分数排在第一的学生姓名为:")
p1.add_run(scoreOrder[0][0]).bold = True
p1.add_run(" 分数为:")
p1.add_run(str(scoreOrder[0][1])).italic = True
p2 = document.add_paragraph("共有:")
p2.add_run(str(len(scoreOrder))).bold = True
p2.add_run(" 名学生参加了考试,学生考试的总体情况:")
table = document.add_table(rows=1, cols=2)
table.style = 'Medium Grid 1 Accent 1'
hdr_cells = table.rows[0].cells
hdr_cells[0].text = '学生姓名'
hdr_cells[1].text = '学生分数'
for studentName, studentScore in scoreOrder:
row_cells = table.add_row().cells
row_cells[0].text = studentName
row_cells[1].text = str(studentScore)
document.add_picture(picPath, width=Inches(6))
document.save('学生成绩报告.docx')
if __name__ == "__main__":
nameList, scoreList = GetExcelInfo()
scoreDictionary = dict(zip(nameList, scoreList))
scoreOrder = sorted(scoreDictionary.items(), key=lambda x: x[1], reverse=True)
GenerateScorePic(scoreOrder)
picPath = "studentScore.jpg"
GenerateScoreReport(scoreOrder, picPath)
print("任务完成,报表生成完毕!")
from docx import Document
if __name__ == "__main__":
document = Document('6 学生成绩报告.docx')
for p in document.paragraphs:
print("paragraphs:", p.text)
for p in document.paragraphs:
if p.style.name == 'Heading 1':
print("Heading 1:", p.text)
for p in document.paragraphs:
if p.style.name == 'Normal':
print("Normal:", p.text)
document.save('修改后的报告.docx')
from docx import Document
if __name__ == "__main__":
document = Document('6 学生成绩报告.docx')
for tb in document.tables:
for i, row in enumerate(tb.rows):
for j, cell in enumerate(row.cells):
text = ''
for p in cell.paragraphs:
text += p.text
print(f'第{i}行,第{j}列的内容{text}')
document.save('修改后的报告.docx')
from docx import Document
if __name__ == "__main__":
document = Document('6 学生成绩报告.docx')
for p in document.paragraphs:
p.text = "修改后的段落内容"
for tb in document.tables:
for i, row in enumerate(tb.rows):
for j, cell in enumerate(row.cells):
text = ''
for p in cell.paragraphs:
p.text = ("第", str(i), "行", str(j), "列")
print(f'第{i}行,第{j}列的内容{text}')
document.save('6.4 修改后的报告.docx')
在 Word 中添加内容框架,插入域并设置域名。
from mailmerge import MailMerge
template = '薪资证明模板.docx'
document = MailMerge(template)
document.merge(
name='唐星',
id='1010101010',
year='2020',
salary='99999',
job='嵌入式软件开发工程师'
)
document.write('生成的 1 份证明.docx')
from mailmerge import MailMerge
from datetime import datetime
def GenerateCertify(templateName, newName):
document = MailMerge(templateName)
document.merge(
name='唐星',
id='1010101010',
year='2020',
salary='99999',
job='嵌入式软件开发工程师'
)
document.write(newName)
if __name__ == "__main__":
templateName = '薪资证明模板.docx'
startTime = datetime.now()
for i in range(10000):
newName = f'./10000 份证明/薪资证明{i}.docx'
GenerateCertify(templateName, newName)
endTime = datetime.now()
allSeconds = (endTime - startTime).seconds
print("生成 10000 份合同一共用时:", str(allSeconds), "秒")
print("程序结束!")

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online