美文网首页
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