美文网首页
python模拟office邮件合并生成成绩单

python模拟office邮件合并生成成绩单

作者: 少儿创客 | 来源:发表于2020-01-17 11:54 被阅读0次

计算思维中识别日常生活中的模式,识别日常生活中的模式是一项具有普遍需求的技能,无论是复盘找规律总结方法论,信息化建设中总结标准流程,建设线上流程;发现生活中的重复性工作等.

需求

在word中有一项很强大的功能:邮件合并,可以方便的生成请柬,工资条,信封等,避免了简单低效的重复操作,这个属于word中比较实用的功能,计算机二级MS Office高级应用中也会用到.但是邮件合并只能从excel中抽取数据,批量生成word,如果有一部分有规律的word,邮件合并就没有办法了.

我们可以用python读取excel数据,并批量生成word,下面我们就讲解如何实现.

数据转换

会把excel中的成绩数据,根据上图中间的模板,生成每个学生的成绩单,而完成上述任务只需要不到20行代码:


18行代码

用字符串模拟批量生成

实际上,请柬,信封,作业本封面都是有一个固定的模板,然后填写必要的信息.模板应用非常广泛,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函数使用的话,可以识别下列二维码:

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
姓名

获取工作表Sheet1 图解

第一个学生张三是第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模板

docxtpl库简单案例
pip install docxtpl

解决了数据源的问题,我们来研究如何生成word,实际上我们在邮件合并的时候,就是要有一个word模板,然后根据word模板插入相关的域,就是可以用邮件合并生成数据了,实际上这里也许要一个word模板,word模板如下

要生成word模板,需要用到一个第三方库,python强大的原因之一,就是有各种第三方库可以满足我们的需求.

word模板
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文件,如下图:


运行结果1
生成的结果2

可以看到跟之前字符串的例子是类似的,数据自动的填充到了word模板之中.

字典

注意之前我们渲染字符串的时候,通过向字符串的format方法,传递变量,就可以完成字符串的填充.而渲染模板则要复杂的多,首先我们用python中的字典保存了需要填充到模板中的变量,

字典保存数据
这个跟JavaScript中的json数据格式有些类似的.python中字典的键值可以字符串,然后冒号后面是对应的键的值,比如student键的值是赵六,然后这些值会通过doc对象的render方法填充到word模板:
模板
word模板的写法是,用两个大括号包裹变量,习惯上变量两边有个空格,如{{ student }},data字典中student键值对应的值会被doc对象的render方法填充到{{ student }},也就是word模板中,更加详细的关系如下图
数据转换

包括注释和空行在内不到30行代码,就可以很轻松的生成成绩单了.当然代码是可以更加精简的,但是会更难以理解.


18行代码

docx-mailmerge库

python有丰富的第三方库,实际上早已经有人想到用python模拟邮件合并,并专门写了一个库docx-mailmerge,但是这个库需要用户熟悉邮件合并,并有插入域的操作,不如直接在word里用jinja2的语法生成模板来的方便.所以不予采用.但是这个方法不需要理解字典这种数据结构.

小结

通过不到30行代码,哪怕是成千上万条数据,也可以方便的用python生成成绩报告了.

扩展

其实python读取word/excel/ppt和生成word/excel/ppt都是很容易的,而且根据成绩生成图表也是很容的,所以往成绩单插入折线图/雷达图也是很容易的,也可以读取别的word中的内容插入到成绩单,或者读取excel的数据,批量插入到ppt中.

相关文章

网友评论

      本文标题:python模拟office邮件合并生成成绩单

      本文链接:https://www.haomeiwen.com/subject/yvdczctx.html