计算思维中识别日常生活中的模式,识别日常生活中的模式是一项具有普遍需求的技能,无论是复盘找规律总结方法论,信息化建设中总结标准流程,建设线上流程;发现生活中的重复性工作等.
需求
在word中有一项很强大的功能:邮件合并,可以方便的生成请柬,工资条,信封等,避免了简单低效的重复操作,这个属于word中比较实用的功能,计算机二级MS Office高级应用中也会用到.但是邮件合并只能从excel中抽取数据,批量生成word,如果有一部分有规律的word,邮件合并就没有办法了.
我们可以用python读取excel数据,并批量生成word,下面我们就讲解如何实现.
会把excel中的成绩数据,根据上图中间的模板,生成每个学生的成绩单,而完成上述任务只需要不到20行代码:
用字符串模拟批量生成
实际上,请柬,信封,作业本封面都是有一个固定的模板,然后填写必要的信息.模板应用非常广泛,office办公软件中就存在巨大的模板,我们平时也会下载各种模板,模板会为我们节约大量时间提高效率;在现在的web开发中,前端页面大多是用模板生成的,比如flask框架的jinjia2模板.
单个同学的例子
比如,我们要发奖状
student = '张三'
template = "恭喜{}同学成为三好学生"
print(template.format(student))
程序运行结果如下:
D:\写作>C:/Users/xpro/AppData/Local/Programs/Python/Python37-32/python.exe d:/写作/01email.py
恭喜张三同学成为三好学生
我们把template变量看做是奖状,name变量代表的是获奖的同学张三,最后输出了获奖信息,恭喜张三同学成为三好学生
.
python中一切皆对象, 字符串也是对象, format是字符串对象的一个方法,作用是把参数填充到字符串中用大括号标记的地方,比如
"恭喜{}同学成为三好学生".format("张三")
, 就是把用format方法把学生姓名张三
填充到模板字符串"恭喜{}同学成为三好学生"
中的大括号, 字符串"恭喜{}同学成为三好学生"
以张三
为参数调用format
方法之后,就会变成了恭喜张三同学成为三号学生
.
不熟悉format
函数使用的话,可以识别下列二维码:
这是一个非常简单的例子,但是却是python实现邮件合并的原型,当我们根据数据和模板批量生成文件的时候,需要的不外乎大量的姓名列表,一个模板,然后输出,只不过细节可能会更多一些.假设我们有一个列表,列表里有很多姓名(可以来自excel或者数据库或者csv文件),然后循环执行生成模板的代码就好了,距离如下:
students = ['张三', '李四', '王五', '赵六'] # 数据源
template = "恭喜{}同学成为三好学生" # 模板
for student in students: # 循环
print(template.format(student)) # 根据模板生成文件
运行结果如下:
D:\写作>C:/Users/xpro/AppData/Local/Programs/Python/Python37-32/python.exe d:/写作/01email.py
恭喜张三同学成为三好学生
恭喜李四同学成为三好学生
恭喜王五同学成为三好学生
恭喜赵六同学成为三好学生
在这个例子里,我们简单地打印字符串,代替需要批量生成的文件,比如生成的word,ppt或者生成图片,但是跟把大象放冰箱,总共分几步一样,一共分为,准备数据源,准备模板,根据数据和模板批量生成三个步骤:
- 准备数据源
- 准备模板
- 根据数据和模板批量生成
是不是很简单.
虚拟数据
对于python不是很熟悉的老师,可以跳过虚拟数据这部分.
每次用列表生成示例数据是很麻烦的,我们结束一个库来帮我们生成虚拟的姓名数据,我们要用到一个叫做faker
的python第三方库,安装方式是
pip install faker
用faker库生成姓名
虚拟姓名
Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from faker import Faker
>>> fake = Faker('zh_CN') # 指定local所属地区中国
>>> fake.name()
'苏红霞'
>>> fake.name()
'董雷'
>>>
在交互式命令行中,我们可以很容易的看到,faker库会帮我们生成虚拟的姓名,但是符合我们中国人的姓名习惯的.
生成虚拟地址
>>> fake.address()
'江西省合山县东丽刘街k座 806555'
>>> fake.address()
'重庆市柳县沙湾吴街P座 708245'
>>>
改写生成奖状的程序如下
from faker import Faker
fake = Faker('zh_CN')
template = "恭喜{}同学成为三好学生" # 模板
for i in range(5):
student = fake.name()
print(template.format(student))
因为可以用faker库生成姓名,所以不再需要姓名列表,你可以理解为我们的数据源从students列表变成了faker库,运行结果如下
D:\写作>python 01email.py
恭喜杨林同学成为三好学生
恭喜王成同学成为三好学生
恭喜刘瑞同学成为三好学生
恭喜王玉同学成为三好学生
恭喜郭欣同学成为三好学生
这里我用了python命令执行了01email.py文件,这里需要注意的是faker库每次调用生成的数据不通,我第二次运行结果如下:
D:\写作>python 01email.py
恭喜林峰同学成为三好学生
恭喜鲍莹同学成为三好学生
恭喜祁婷同学成为三好学生
恭喜惠雪梅同学成为三好学生
恭喜卢秀芳同学成为三好学生
读取excel数据
读取并处理excel数据,最好用的就是numpy库了,不过numpy不如xlrd库直观,我们先用xlrd来写示例程序.这次的数据源是excel,是一个有姓名,语数英成绩的excel表,当前目录下的data.xlsx文件,数据在excel文件的Sheet1工作表中.
import xlrd
# 用xlrd模块的open_workbook方法打开excel文件
# 类似于我们在wps,office的打开操作
# 参数是文件的路径,字符串前面的r表示路径
xls = xlrd.open_workbook(r'data.xlsx')
# 通过工作表的名字Sheet1获取工作表1
sheet1 = xls.sheet_by_name('Sheet1')
# 第一行第一列是姓名表头
# 但是python中是从0开始计数
# student的值是姓名
student = sheet1.cell_value(0, 0)
print(student)
# 运行结果是: 姓名
运行结果如下
D:\写作>python 01email.py
姓名
第一个学生张三是第2行第1列的数据,读取方法如下
import xlrd
# 用xlrd模块的open_workbook方法打开excel文件
# 类似于我们在wps,office的打开操作
# 参数是文件的路径,字符串前面的r表示路径
xls = xlrd.open_workbook(r'data.xlsx')
# 通过工作表的名字Sheet1获取工作表1
sheet1 = xls.sheet_by_name('Sheet1')
# 第一行第一列是姓名表头
# 但是python中是从0开始计数
# student的值是姓名
# cell_value方法获取单元格的值
# 第一个参数代表行序号,第二个参数代表列序号
student = sheet1.cell_value(1, 0)
print(student)
# 运行结果是: 姓名
运行结果
D:\写作>python 01email.py
张三
当我们知道了如何读取姓名,也就知道了如何读取语数英的成绩分别是
chinese = sheet1.cell_value(1, 1)
math = sheet1.cell_value(1, 2)
english = sheet1.cell_value(1, 3)
接下来我们输出张三同学的成绩单:
import xlrd
# 读取数据源
xls = xlrd.open_workbook(r'data.xlsx')
sheet1 = xls.sheet_by_name('Sheet1')
# 模板
template = "{}同学的成绩如下,语文{},数学{},英语{}"
student = sheet1.cell_value(1, 0)
chinese = sheet1.cell_value(1, 1)
math = sheet1.cell_value(1, 2)
english = sheet1.cell_value(1, 3)
# 根据模板输出信息
print(template.format(student, chinese, math, english))
运行结果如下:
D:\写作>python 01email.py
张三同学的成绩如下,语文89.0,数学100.0,英语100.0
接下来我们只需要用循环读取数据就可以了,有个问题,excel中有多少行数据呢?可以用工作表的nrows获取行数,需要注意的是excel的第一行不是数据而是表头,循环的时候需要跳过.
import xlrd
# 读取数据源
xls = xlrd.open_workbook(r'data.xlsx')
sheet1 = xls.sheet_by_name('Sheet1')
# 模板
template = "{}同学的成绩如下,语文{},数学{},英语{}"
rows = sheet1.nrows # 表格行数
for i in range(1, rows): # 跳过表头一行
student = sheet1.cell_value(i, 0) # 第i行代表第i行数据
chinese = sheet1.cell_value(i, 1)
math = sheet1.cell_value(i, 2)
english = sheet1.cell_value(i, 3)
# 根据模板输出信息
print(template.format(student, chinese, math, english))
运行结果如下:
D:\写作>python 01email.py
张三同学的成绩如下,语文89.0,数学100.0,英语100.0
李四同学的成绩如下,语文92.0,数学99.0,英语98100.0
王五同学的成绩如下,语文95.0,数学97.0,英语88.0
赵六同学的成绩如下,语文98.0,数学98.0,英语78.0
word模板
pip install docxtpl
解决了数据源的问题,我们来研究如何生成word,实际上我们在邮件合并的时候,就是要有一个word模板,然后根据word模板插入相关的域,就是可以用邮件合并生成数据了,实际上这里也许要一个word模板,word模板如下
要生成word模板,需要用到一个第三方库,python强大的原因之一,就是有各种第三方库可以满足我们的需求.
import xlrd
from docxtpl import DocxTemplate
# 读取数据源,打开word
xls = xlrd.open_workbook(r'data.xlsx')
sheet1 = xls.sheet_by_name('Sheet1')
rows = sheet1.nrows # 表格行数
# 读取数据并生成文件
for i in range(1, rows): # 跳过表头一行
student = sheet1.cell_value(i, 0) # 第i行代表第i行数据
chinese = sheet1.cell_value(i, 1)
math = sheet1.cell_value(i, 2)
english = sheet1.cell_value(i, 3)
# 根据模板输出信息
# 打开一个模板
doc = DocxTemplate(r"score.docx")
data = {} # 构造填充模板需要的数据
data['student'] = student
data['chinese'] = chinese
data['math'] = math
data['english'] = english
doc.render(data) # 填充数据data到模板
doc.save("{}.docx".format(student)) # 根据模板为每个学生生成成绩单
程序运行后,会在当前目录下生成以学生名字命名的word文件,如下图:
可以看到跟之前字符串的例子是类似的,数据自动的填充到了word模板之中.
字典
注意之前我们渲染字符串的时候,通过向字符串的format方法,传递变量,就可以完成字符串的填充.而渲染模板则要复杂的多,首先我们用python中的字典保存了需要填充到模板中的变量,
这个跟JavaScript中的json数据格式有些类似的.python中字典的键值可以字符串,然后冒号后面是对应的键的值,比如student键的值是赵六,然后这些值会通过doc对象的render方法填充到word模板:
word模板的写法是,用两个大括号包裹变量,习惯上变量两边有个空格,如
{{ student }}
,data字典中student
键值对应的值会被doc对象的render
方法填充到{{ student }}
,也就是word模板中,更加详细的关系如下图包括注释和空行在内不到30行代码,就可以很轻松的生成成绩单了.当然代码是可以更加精简的,但是会更难以理解.
docx-mailmerge库
python有丰富的第三方库,实际上早已经有人想到用python模拟邮件合并,并专门写了一个库docx-mailmerge
,但是这个库需要用户熟悉邮件合并,并有插入域的操作,不如直接在word里用jinja2
的语法生成模板来的方便.所以不予采用.但是这个方法不需要理解字典这种数据结构.
小结
通过不到30行代码,哪怕是成千上万条数据,也可以方便的用python生成成绩报告了.
扩展
其实python读取word/excel/ppt和生成word/excel/ppt都是很容易的,而且根据成绩生成图表也是很容的,所以往成绩单插入折线图
/雷达图
也是很容易的,也可以读取别的word中的内容插入到成绩单,或者读取excel的数据,批量插入到ppt中.