Python实现办公自动化——自动编写word文档
- 前言
- 安装python-docx
- python-docx使用
- 创建word文档
- 设置纸张方向、大小和页边距
- 统一设置格式
- 插入文本
- 插入表格
- 插入图片
- 结语
前言
工作中有大量的报告编写需求,在不停地复制粘贴之后,突然想到,这种高度重复的工作有没有编程可以实现的方法呢?经过查找相关内容发现一个关键词叫做RPA(机器人流程自动化,各位去搜索一下这个关键词,会发现和我想要实现的需求完全一致,只是这个词一般在财务金融之类的环境中提起),那么python可以实现RPA吗?继续搜索发现有很多包,这里介绍一下python-docx,一个用来生成word文档的包。官方文档链接python-docx。
安装python-docx
使用pip就可以安装了,如果下载速度慢,需要替换为国内镜像源:
pip install python-docx
python-docx使用
创建word文档
from docx import Document
document = Document()
document.save("报告.docx")
使用Document()就完成了一个word文档的创建,我起的变量名叫document,这个步骤相当于在文件夹里鼠标右键新建了一个空的word文档。
document很重要,相当于一个还没有装水的大池子,我们所有插入内容的内容都要灌进这个池子里,专业的说法document是新建的一个对象,所有操作都要使用这个对象(document后面一个点,再加上具体要调用的方法),document操作完之后,最后一步记得调用save()方法保存文档,可以是相对路径也可以是绝对路径,如果使用相对路径,程序所在路径是根目录。
下面所有的代码都应该放在document = Document()和document.save(“报告.docx”)之间,就不再重复给出了。
设置纸张方向、大小和页边距
熟悉word操作大家肯定知道分节符,每一节中页眉页脚和边距等都是统一的。新建的document中默认有一个分节符,想要设置第一节的纸张方向和页边距,就要获取分节符对象。下面代码中section就是获取到的分节符对象,如果有好几个分节符,0代表第一个,依此类推。
import docx.shared
from docx.enum.section import WD_ORIENTATION
""" 获取第一个分节符 """
section = document.sections[0]
""" 设置横向 """
section.orientation = WD_ORIENTATION.LANDSCAPE
# 设置页面
page_h, page_w = section.page_width, section.page_height # 高度和宽度颠倒一下
# 设置横向纸的宽度
section.page_width = page_w
# 设置横向纸的高度
section.page_height = page_h
# 设置上下左右页边距
section.left_margin = docx.shared.Cm(2)
section.right_margin = docx.shared.Cm(2)
section.top_margin = docx.shared.Cm(2)
section.bottom_margin = docx.shared.Cm(2)
word文档有横向和纵向,python-docx设置横向的代码section.orientation = WD_ORIENTATION.LANDSCAPE,如果设置纵向要改为section.orientation = WD_ORIENTATION.PORTRAIT,默认是纵向的,所以一般只有横向才需要代码实现。
如果不设置纸的高度和宽度,你会发现打开的word文档好像还是“纵向”的,实际在打印的时候以及查看布局——纸张方向你会发现确实是横向的,电脑识别到的word文档和我们看起来好像不一致,为了使得观感和纸张方向统一,我们获取纸张的高宽,然后高度设置为宽度,宽度设置为高度,就可以了。
当然,如果你只需要纵向的文档,那么上面的步骤都不需要。
设置这一节内容的上下左右页边距,分别设置section的不同属性即可,python-docx对于各种距离单位,默认使用的是“磅”,也就是说section.left_margin = 2,会将左边距设置为2磅,个人还是习惯厘米做单位,不过这样的话要通过docx.shared.Cm()将厘米转换为磅,上面代码中设置边距为2厘米,通过转换函数转换为了磅。
如果要添加新的分节符:
from docx.enum.section import WD_SECTION_START
section_new = document.add_section(start_type=WD_SECTION_START.NEW_PAGE)
python-docx的函数名起的都很好理解,上面的代码不解释大家也能明白,调用document的方法新增了一个section,类型WD_SECTION_START可以选择NEW_PAGE下一节,也可以选择连续等分节符。
统一设置格式
python-docx添加的图表文字等内容都可以在"add"后再修改格式,但是这样的话文字每次add之后,都要多好几行代码去设置行距、字体、缩进等,太繁琐了,python-docx可以和word一样设置样式,add完文字后,将样式应用到文字上即可。可以新建样式,也可以修改已有样式。
""" 创建正文样式 """
from docx.oxml.ns import qn
from docx.enum.style import WD_STYLE_TYPE
from docx.shared import Pt, Cm
style_normal = document.styles.add_style('NORMAL STYLE', WD_STYLE_TYPE.PARAGRAPH)
style_normal.base_style = document.styles['Normal'] # 基本样式
style_normal.font.name = 'Times New Roman' # 英文字体
style_normal.element.rPr.rFonts.set((qn('w:eastAsia')), '宋体') # 中文字体
style_normal.paragraph_format.space_before = Pt(0) # 段前
style_normal.paragraph_format.space_after = Pt(0) # 段后
style_normal.font.size = Pt(14) # 字号
style_normal.paragraph_format.line_spacing = Pt(28) # 行距
style_normal.paragraph_format.first_line_indent = Pt(28) # 首行缩进
上面的代码新建了一个样式,我起名叫做NORMAL STYLE,它继承自基本样式,然后设置了自己字体和段前段后,行距缩进等,python-docx没有“字符”这个单位,所以我想首行缩进两字符只能自己去算,一个字14磅,那么首行缩进28磅就是两个字符啦。
document.styles['Normal'].font.name = 'Times New Roman' # 英文字体
document.styles['Normal'].element.rPr.rFonts.set((qn('w:eastAsia')), '宋体') # 中文字体
document.styles['Normal'].paragraph_format.space_before = Pt(0)
document.styles['Normal'].paragraph_format.space_after = Pt(0)
document.styles['Normal'].font.size = Pt(14)
document.styles['Normal'].paragraph_format.line_spacing = Pt(28)
document.styles['Normal'].paragraph_format.first_line_indent = Pt(28)
上面的代码修改了已有样式,这里将基本的Normal样式进行了修改,之后代码所有add的文字格式都会按照修改后的Normal样式。
如果新建样式,在每次add之后都要应用一下样式,如果是修改Normal样式,那么add之后不需要应用样式,如果修改其他样式,同样需要add之后应用一下样式,因为默认按照Normal的格式显示。修改Normal样式虽然不需要每次设置正文的样式,但经过作者尝试发现了一个问题,如果word文档中有表格要插入文字的话,无论怎样设置表格中文字的样式,依然会被设置为Normal样式,好像Normal的优先级很高,不是表格中的文字没有这个问题,可以正常应用其他样式,所以word文档中有表格的话,建议新建样式,不要采用修改Normal样式的方法。
word自带了很多样式,不过没必要的话不要继承一些没听过的样式,比如某个样式自带了下划线,代码里怎么设置也去不掉,会有这种情况。
插入文本
word文档有很多段落(paragraph),python-docx也有这个概念,然后进一步的,python-docx的概念中,每个段落又有很多"run"对象。
添加一段文字,可以设置段落整体的行距,首行缩进等,给段落中添加一个run,可以设置这个run里文字的格式,这样就实现了一段文字中,有不同的字体格式。
paragraph = document.add_paragraph("测试段落")
paragraph.style = "NORMAL STYLE"
添加一段文字很简单,添加之后设置style的属性,就可以应用之前添加的样式。每次调用add_paragraph,都相当于按了一次回车键,然后才是文字内容。上面提到,如果一段文字要设置不同格式,可以添加多个run。
paragraph = document.add_paragraph(style="NORMAL STYLE")
run = paragraph.add_run("尊敬的")
run.font.name = "黑体"
run = paragraph.add_run("XX女士/先生")
run.font.name = "宋体"
和设置样式类似,段落以及run都可以设置里面的字体大小等属性进行细节的格式修改。
插入表格
table = document.add_table(rows=5, cols=5, style='NORMAL STYLE')
table.cell(0, 0).text = '测试'
table.rows[1].cells[0].merge(table.rows[1].cells[1])
表格的添加很简单,在新建表格时设置好行列参数,并且可以设置表格内文字的样式,前面提到过,如果修改word自带的Normal样式的话这里的设置样式会失效。table.cell可以获取具体几行几列的单元格,调用.text修改内容。使用merge可以合并单元格,上面的代码表示将第2行第1列单元格和第2行第2列单元格合并(python计数从0开始)。如果有多个单元格合并,只能一个一个进行,我一般是写一个循环实现。如果合并单元格对应的原始单元格有多个填充了文字,那么和excel类似,只会保留第一个单元格的内容。
插入图片
添加图片,同样简单粗暴,add_picture搞定,类似于设置文字的属性一样,我们可以设置图片的一些属性,一般对图片设置主要是设置大小,我获取了图片的高度和宽度(默认单位是磅),然后将磅转换为了厘米,把图片设置为厘米单位的高度宽度。figurepath大家自己替换为图片的文件地址即可。
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
run = paragraph.add_run()
pic = run.add_picture(figurepath)
original_width, original_height = pic.width, pic.height
change_ratio = (7/2.54*914400) / original_height
scaled_width = int(original_width * change_ratio)
scaled_height = int(original_height * change_ratio) # 缩放至7cm
pic.width = scaled_width # 缩放
pic.height = scaled_height # 缩放
paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
结语
python-docx使用十分方便,不过也需要注意,它主要是用来按照代码生成新的word文档的(虽然也可以读取已有word文档,但是功能较弱),如果已有一些word文档,想要读取word文档并在指定位置做修改,要用到更底层的pywin32等包才能实现需求。python-docx本质上是在新建一个文件,只不过这个文件按照word文档的规范编排,如果你的电脑没有安装word程序,python-docx生成word文档依然可以正常执行,只是需要换一台装了word的电脑才可以正常打开阅读,而pywin32等包加载已有的word文档,需要通过运行word程序来操作,必须要安装word程序才能实现,本质上是程序代替你打开word来操作,好处是这样可以进行的格式编排会比python-docx更精细,但是代码会更加底层和复杂。
可能还有的人会问,python-docx插入文字,图,表之类的,和word模板岂不是很类似?如果单从python-docx一个包来看,实现的功能会有点类似,但是python-docx可以与其他工具包配合,将大量数据的计算,绘制图表与word文档结合起来,这样的强大功能是word模板完全无法相比的。鼠标一点,几十页的报告一键生成,这样的便利性只有各位在实际工作中用到了才能真正体会到O(∩_∩)O。