我心中的王者:Python-第17章 使用Python处理Word文件
Word是二进制(binary)文件,同时Word还有字体格式、色彩与版面配置等,所以它的处理方式比起文本文件(txt)要复杂。不过,读者不用担心,笔者将以实例一步一步讲解,相信读完本章读者也可以很轻松学会使用Python处理Word文件。
本章内容需要使用外部模块python-docx,读者可参考附录B下载此模块,尽管模块名称是python-docx,下载时指令是:
pip install python-docx
程序导入使用import时是:
import docx # 这一点需留意,不是python-docx
17-1 从Python看Word文件结构
在python-docx模块内,将Word文件结构分成3层:
- Document这是最高层代表整个Word文件。
- Paragraph一个Word文件是由许多的段落所组成,在Python中整份文件的定义是Document,这些段落的定义就是Paragraph对象。我们使用Word编辑文件时,如果单击一次Enter键,会产生一个新的段落。在Python中一个段落代表一个Paragraph对象,所有段落以Paragraph对象列表(list)方式存在。
- Run Word文件要考虑的有字号、字体样式、色彩等,我们将这些称作样式。一个Run对象所指的是Paragraph对象中相同样式的连续文字,如果文字发生样式变化,Python将以新的Run对象代表。
上图有6个Run。
17-2 读取Word文件内容
17-2-1 建立docx对象
首先需建立Word文件(Document)的对象docx对象,可用Document( )方法建立,wdoc是自行取的名称,本书实例皆以wdoc为Document对象名称。
wdoc = docx.Document(‘文件名') # 建立docx对象wdoc
17-2-2 获得Paragraph和Run数量
可以使用len( )方法获得Paragraph数量。
len(wdoc.paragraphs) # wdoc是前一小节所建的docx对象
下列语法可以获得第n段Paragraph的Run数量。
len(wdoc.paragraphs[n].runs) # n是第几段或称Paragraph编号
17-2-3 列出Paragraph内容
可以使用下列语法打印第n段Paragraph内容。
print(wdoc.paragraphs[n].text)
17-2-4 列出Paragraph内的Run内容
可以使用下列语法打印第n段Paragraph第m个Run内容。
print(wdoc.paragraphs[n].runs[m].text)
17-2-5 读取和打印文件的应用
程序实例ch17_1.py:有一个Word文件data17_1.docx内容如下:
这个程序会做下列几件事:
-
第5行,列出Paragraph的数量,相当于段落的数量。
-
第6和7行,用循环打印各个Paragraph内容,相当于文件内容。
-
第9行,列出Paragraph 1的Run数量。
-
第10和11行,用循环打印Paragraph 1 Run内容。
-
第13行,列出Paragraph 2的Run数量。
-
第14和15行,用循环打印Paragraph 2 Run内容。
# ch17_1.py
import docx
wdoc = docx.Document('data17_1.docx')
print("段落数, 也可称Paragraph物件数量 = ", len(wdoc.paragraphs))
for i in range(0, len(wdoc.paragraphs)):
print("paragraph %d = " % i, wdoc.paragraphs[i].text)
print("Paragraph 1的Run数量 = ", len(wdoc.paragraphs[1].runs))
for i in range(0, len(wdoc.paragraphs[1].runs)):
print("Run %d = " % i, wdoc.paragraphs[1].runs[i].text)
print("Paragraph 2的Run数量 = ", len(wdoc.paragraphs[2].runs))
for i in range(0, len(wdoc.paragraphs[2].runs)):
print("Run %d = " % i, wdoc.paragraphs[2].runs[i].text)
执行结果
段落数, 也可称Paragraph物件数量 = 5
paragraph 0 = 使用Python操作Word
paragraph 1 = 学习Python是一个很好的经验
paragraph 2 = This book is published by Deep Stone.
paragraph 3 = 深石数字科技
paragraph 4 = DeepStone is Deep Learning.
Paragraph 1的Run数量 = 5
Run 0 = 学习
Run 1 = Python
Run 2 = 是一个
Run 3 = 很好的
Run 4 = 经验
Paragraph 2的Run数量 = 3
Run 0 = This book is
Run 1 = published by
Run 2 = Deep Stone.
17-2-6 读取文件与适度编排输出
在操作Word文件时,也可以适度利用一些技巧,执行文件的编排。
程序实例ch17_2.py:这个程序主要是将所读取的Word文件,第一执行首行缩排2个字,第二段落间空一行输出。下列是data17_2.docx内容。
下列是程序内容。
# ch17_2.py
import docx
def getFile(fn):
'''读取档案与适度编辑档案'''
wdoc = docx.Document(fn) # 建立Word档案物
txt = []
for paragraph in wdoc.paragraphs:
print(paragraph.text) # 输出文件所读取的Paragraph内容
txt.append(' ' + paragraph.text) # 内缩同时将每一段Paragraph组成列表
return '\n\n'.join(txt) # 将列表组成字符串同时段落隔行输出
print(getFile('data17_2.docx'))
执行结果
Word是二进制(binary)档案,同时Word还有字型格式、色彩与版面配置 … 等,所以它的处理方式比起文本文件(txt)要复杂。
本章内容需要使用外挂模块python-docx,读者可参考附录B下载此模块,尽管模块名称是python-docx,下载时指令是:
Word是二进制(binary)档案,同时Word还有字型格式、色彩与版面配置 … 等,所以它的处理方式比起文本文件(txt)要复杂。
本章内容需要使用外挂模块python-docx,读者可参考附录B下载此模块,尽管模块名称是python-docx,下载时指令是:
上述程序第8和9行是读取段落Paragraph内容后直接输出,可以得到前4行的输出结果。第8行至第10行是一个循环,第10行的功能是在段落Paragraph前方加上4个英文字符宽度,未来输出时会产生缩排2个中文字宽度的效果,第10行另外功能是将输出的段落Paragraph组成列表(list)。
程序第11行的join( )功能可以参考6-6-3小节,这个功能是将第10行建立的列表元素组织起来变成字符串,各字符串元素间使用‘\n\n\’分隔,这是2个换行符号,第一个‘\n’可以让下一个元素换行输出,第二个‘\n’就可以产生空一行输出的结果。
17-3 存储文件
save( )方法可以存储Document对象的文件,如果将建立Word文件与存储Word文件整个语法串连,整个语法如下:
程序实例ch17_3.docx:将文件data17_1.docx复制入out17_3.docx。
# ch17_3.py
import docx
wdoc = docx.Document('data17_1.docx')
wdoc.save('out17_3.docx')
执行结果 在目前文件夹可以建立与data17_1.docx相同内容的out17_3.docx。
17-4 建立文件内容
17-4-1 建立标题
可以使用下列add_heading( )方法建立文件标题内容。
wdoc.add_heading(‘content_of_heading') # wdoc是自建的文件对象
上述预设会建立Heading 1的标题,Word的标题有1-9,如果想建立不同的标题可以使用第2个参数‘level=n’。
可以使用下列语法在建立文件标题内容同时设定标题格式。
wdoc.add_heading(‘content_of_heading', level=n) # wdoc是自建的文件对象
程序实例ch17_4.docx:建立Word标题的应用,这个程序会输出标题,同时将所建的Word文件存入out17_4.docx。
# ch17_4.py
import docx
wdoc = docx.Document()
wdoc.add_heading('明志科大') # 预设是标题1格式
wdoc.add_heading('明志科大', level=2) # 标题2格式
wdoc.add_heading('明志科大', level=3) # 标题3格式
print(wdoc.paragraphs[0].text)
print(wdoc.paragraphs[1].text)
print(wdoc.paragraphs[2].text)
wdoc.save('out17_4.docx')
执行结果 下列是Python Shell窗口执行结果与out17_4.docx。
明志科大
明志科大
明志科大
由于Python Shell是文本模式的窗口,所以执行结果的输出看不出标题效果,但是若是用Word打开,可以看到标题效果,同时将插入点放在标题1字符串,可以在常用/样式看到标题1选项。
17-4-2 建立段落Paragraph内容
可以使用add_paragraph( )方法建立文件的段落(也可称Paragraph)内容。
ptr = wdoc.add_paragraph(‘paragraph_content') # ptr是段落对象
上述ptr可以自行命名用于储存段落对象,这是可有可无的,但是如果有了这个段落对象,未来插入段落时可以将新段落插入此段落的前面,或是将Run内容插入此段落内。可以使用insert_paragraph_before( )方法,将段落插在上述ptr段落对象的前方,可参考程序实例ch17_5.py第9行。
程序实例ch17_5.docx:先插入2个段落,然后将新段落插在第一个段落的前面。
# ch17_5.py
import docx
wdoc = docx.Document()
ptr = wdoc.add_paragraph('我是第1段落') # 回传ptr段落物件
print(wdoc.paragraphs[0].text)
wdoc.add_paragraph('我是第2段落') # 不加回传也可以
print(wdoc.paragraphs[1].text)
prior_ptr = ptr.insert_paragraph_before('我是新的第1段落') # 新段落插在ptr前面
print(wdoc.paragraphs[0].text)
wdoc.save('out17_5.docx')
执行结果 下列是Python Shell窗口执行结果与out17_5.docx。
我是第1段落
我是第2段落
我是新的第1段落
将插入点放在Paragraph[0]字符串,可以在常用/样式看到正文选项,正文选项是默认插入文件内容的样式(在17-6节,笔者会介绍插入Paragraph时同时处理样式)。
17-4-3 建立Run内容
Paragraph是由Run组成,当我们建立Paragraph成功后,未来若是想要在Paragraph内插入内容,可以使用add_run( )方法,此方法的语法格式如下:
ptr.add_run(‘run_content') # ptr是段落对象, 插入run内容
程序实例ch17_6.py:先建立一个段落,然后将Run加在这个段落内。
# ch17_6.py
import docx
wdoc = docx.Document()
ptr = wdoc.add_paragraph('我是第1段落') # 回传ptr段落物件
print(wdoc.paragraphs[0].text) # 打印段落
ptr.add_run('第一个run内容') # 将run加入ptr段落对象
ptr.add_run('第二个新run内容') # 将run加入ptr段落对象
print(wdoc.paragraphs[0].text) # 打印段落
wdoc.save('out17_6.docx')
执行结果 下列是Python Shell窗口执行结果与out17_6.docx。
我是第1段落
我是第1段落第一个run内容第二个新run内容
17-4-4 强制换页输出
下列add_page_break( )方法可以强制Word换页。
wdoc.add_page_break( )
未来如果有插入段落时,会在新一页出现。
程序实例ch17_7.docx:强制换页输出的应用。
# ch17_7.py
import docx
wdoc = docx.Document()
ptr = wdoc.add_paragraph('我是第1段落') # 回传ptr段落物件
wdoc.add_paragraph('我是第2段落') # 不加回传也可以
wdoc.add_page_break() # 插入换页
wdoc.add_paragraph('我是新的页开始段落') # 新段落插在新的一页
wdoc.save('out17_7.docx')
执行结果 下列是out17_7.docx第一页与第二页的结果。
17-4-5 插入图片
可以使用add_picture( )方法插入图片到Word文件内,如下所示:
wdoc.add_picture(‘image_file')
如果插入图片时想要设定图片的宽度或高度,需导入docx.shared模块,然后就可以在add_picture( )方法内增加使用第2个参数width(宽度)或height(高度),然后用Inches( )英寸函数或Cm( )公分函数设定图片宽度。
from docx.shared import Inches
wdoc.add_picture(‘image_file', width=Inches(宽度值))
程序实例ch17_8.docx:插入图片的应用,图片宽度是1.0英寸。
# ch17_8.py
import docx
from docx.shared import Inches
wdoc = docx.Document()
wdoc.add_paragraph('晓波相片')
wdoc.add_picture('晓波.jpg', width=Inches(1.0))
wdoc.save('out17_8.docx')
执行结果 下列是out17_8.docx内容。
17-5 建立表格
add_table( )方法可以建立表格。
table = wdoc.add_table(rows=?, cols=?) # 执行完后返回table表格对象
17-5-1 建立表格内容
建议可以一次处理一列的表格内容,如下所示:
row = table.rows[0]
row.cells[0].text = ‘表格(0, 0)内容'
row.cells[1].text = ‘表格(0, 1)内容'
程序实例ch17_9.py:建立一个2 rows和2 cols的表格。
# ch17_9.py
import docx
wdoc = docx.Document()
table = wdoc.add_table(rows=2, cols=2)
row = table.rows[0] # 建立row 0表格数据
row.cells[0].text = '书号'
row.cells[1].text = '我的著作'
row = table.rows[1] # 建立row 1表格数据
row.cells[0].text = 'XA1711'
row.cells[1].text = 'HTML5+CSS3王者归来'
wdoc.save('out17_9.docx')
执行结果 下列是out17_9.docx内容。
17-5-2 插入表格列
可以使用add_row( )插入表格列。
程序实例ch17_10.py:重新设计ch17_9.py,插入表格列和输入表格列数据。
# ch17_10.py
import docx
wdoc = docx.Document()
table = wdoc.add_table(rows=2, cols=2)
row = table.rows[0]
row.cells[0].text = '书号'
row.cells[1].text = '我的著作'
row = table.rows[1]
row.cells[0].text = 'XA1711'
row.cells[1].text = 'HTML5+CSS3王者归来'
# 增加表格列和输入数据
new_row = table.add_row() # 增加表格行
new_row.cells[0].text = 'XA1774'
new_row.cells[1].text = 'Python王者归来'
wdoc.save('out17_10.docx')
执行结果 下列是out17_10.docx内容。
17-5-3 计算表格的rows和cols的长度
可以使用len( )函数计算表格的rows和cols的长度。
程序实例ch17_11.py:计算程序实例ch17_10.py所建表格的rows和cols的长度。
# ch17_11.py
import docx
wdoc = docx.Document()
table = wdoc.add_table(rows=2, cols=2)
row = table.rows[0]
row.cells[0].text = '书号'
row.cells[1].text = '我的著作'
row = table.rows[1]
row.cells[0].text = 'XA1711'
row.cells[1].text = 'HTML5+CSS3王者归来'
# 增加表格行和输入数据
new_row = table.add_row() # 增加表格行
new_row.cells[0].text = 'XA1774'
new_row.cells[1].text = 'Python王者归来'
print('rows = ', len(table.rows)) # 计算和打印rows
print('cols = ', len(table.columns)) # 计算和打印cols
执行结果
rows = 3
cols = 2
17-5-4 打印表格内容
可以使用双层循环打印表格内容,细节可参考下列实例ch17_12.py第17至19行。
程序实例ch17_12.py:打印ch17_10.py所建的表格内容。
# ch17_12.py
import docx
wdoc = docx.Document()
table = wdoc.add_table(rows=2, cols=2)
row = table.rows[0]
row.cells[0].text = '书号'
row.cells[1].text = '我的著作'
row = table.rows[1]
row.cells[0].text = 'XA1711'
row.cells[1].text = 'HTML5+CSS3王者归来'
# 增加表格行和输入数据
new_row = table.add_row() # 增加表格行
new_row.cells[0].text = 'XA1774'
new_row.cells[1].text = 'Python王者归来'
# 双层循环打印表格
for row in table.rows:
for cell in row.cells:
print(cell.text)
执行结果
书号
我的著作
XA1711
HTML5+CSS3王者归来
XA1774
Python王者归来
17-5-5 表格的样式
先前所打印的表格没有框线,可以使用table.style设定框线。
程序实例ch17_13.py:使用框线样式‘LightShading-Accent1’设定程序ch17_10.py所建立的表格。
# ch17_13.py
import docx
wdoc = docx.Document()
table = wdoc.add_table(rows=2, cols=2)
row = table.rows[0]
row.cells[0].text = '书号'
row.cells[1].text = '我的著作'
row = table.rows[1]
row.cells[0].text = 'XA1711'
row.cells[1].text = 'HTML5+CSS3王者归来'
# 增加表格行和输入数据
new_row = table.add_row() # 增加表格行
new_row.cells[0].text = 'XA1774'
new_row.cells[1].text = 'Python王者归来'
table.style = 'LightShading-Accent1' # 设定表格样式
wdoc.save('out17_13.docx')
执行结果 这个程序执行时有Warning信息,可以不必理会,下列是out17_13.docx内容。
如果打开表格样式表单将鼠标光标移至样式表单,可以看到各个样式表单名称,不过目前笔者写此书时的python-docx 0.86版本尚未支持中文样式表单。上述第16行是设定表格的样式,LightShading是浅色底纹,Accent1是辅色1,若是调整为Accent2,……,Accent6将有不同的结果。
17-6 Paragraph样式
Paragraph样式就是所谓的段落样式,下列是常见的Word样式内容。
上述list、listBullet、ListNumber如果是编号1可以省略编号,如果是编号2和3则可以标明。Heading则是由Heading1、……、Heading9所组成。我们在插入段落时,可以在add_paragraph( )方法内增加第2个参数“style=样式名称”,这样就可以在插入段落同时设定段落的样式,可参考下列程序第5行。
程序实例ch17_14.py:建立段落时,同时设定段落的样式。
# ch17_14.py
import docx
wdoc = docx.Document()
wdoc.add_paragraph('明志科大', style='ListNumber') # ListNumber
wdoc.add_paragraph('长庚科大', style='ListNumber') # ListNumber
wdoc.add_paragraph('长庚大学', style='ListNumber') # ListNumber
wdoc.add_paragraph('明志科大', style='ListBullet') # ListBullet
wdoc.add_paragraph('长庚科大', style='ListBullet') # ListBullet
wdoc.add_paragraph('长庚大学', style='ListBullet') # ListBullet
wdoc.save('out17_14.docx')
执行结果 执行时会有Warning消息。
17-7 Run的样式
Run的样式重点就是设定Run的文字(text)属性,下列是常见的属性。
bold(粗体) italic(斜体) underline(下画线) strike(删除线)
当我们建立一个Run对象时,会回传Run对象,此时若将此对象的样式设为True,相当于可以建立该Run对象的样式。整个细节读者可以参考第4-7行。
程序实例ch17_15.py:建立Run内容,然后设定此内容的属性为粗体(bold)与斜体(italic)的应用。
# ch17_15.py
import docx
wdoc = docx.Document()
ptr = wdoc.add_paragraph('我是第1段落') # 回传ptr段落物件
run1 = ptr.add_run('我是粗体') # 将run加入ptr段落对象
run1.bold = True # 设定粗体
run2 = ptr.add_run('我是斜体') # 将run加入ptr段落对象
run2.italic = True # 设定斜体
wdoc.save('out17_15.docx')
执行结果
17-8 综合应用——抢救CIA情报员
在16-7-2小节笔者有介绍这个范例,当时文字适用msg字符串方式表示,在真实的应用中所有文件皆是以Word方式表达,这一节将用读取Word文件方式重新设计ch16_37.py。
程序实例ch17_16.py:重新设计ch16_37.py,下列是data17_16.docx的内容。
下列是程序内容。
# ch17_16.py
import docx
import re
wdoc = docx.Document('data17_16.docx')
txt = docx.Document()
pattern = r'CIA (\w)\w*' # 欲搜寻FBI + 空一格后的名字
newstr = r'\1***' # 新字符串使用隐藏文字
txt.add_paragraph(re.sub(pattern,newstr,wdoc.paragraphs[0].text))
txt.save('out17_16.docx')
执行结果 下列是out17_16.docx的内容。
更多有关Python-docx的用法,读者可参考下列Python官方网页。
https://python-docx.readthedocs.io/en/latest/