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

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

需求

在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中.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • python学习笔记 声明:学习笔记主要是根据廖雪峰官方网站python学习学习的,另外根据自己平时的积累进行修正...
    renyangfar阅读 3,016评论 0 10
  • 当年的上天梯三姐妹,可是南湖水大坝边上一道靓丽的风景,我陪衬着也靓丽一下。而今时光飞逝,转眼二十载,今日喜相聚,大...
    DAN524阅读 167评论 0 0
  • 昨晚睡不着时我又在看《流星花园》的搞笑片段,恍恍惚惚我感觉我又回到了大一暑假,那时我在观内外学手绘,二姐刚失恋又每...
    Hazel马阅读 233评论 0 0
  • 自古逢秋悲长空,登临石南大不同。 斜阳草树雾岚轻,寻常巷陌禅意浓。 地振高岗挽卢弓,门朝清溪护苍龙。 鱼池山月来相...
    朱二哥的一些事一些情阅读 191评论 0 6