python实现Excel邮件合并

15 篇文章 2 订阅


众所周知word/WPS中的邮件合并功能可以根据word模板从excel表格中读取数据,进而生成不同的文件

但Excel却没有这个功能,如果需要根据excel表格模板生成不同文件的话,手动填写就显得很麻烦

举例:需对1、2图片模板表格的2个sheet中所有’{姓名}‘、’{案卷号}'修改为第3个图片表格的数据,按姓名保存为不同文件
模板表格sheet1

模板表格sheet2
数据表格
使用程序读取数据excel表格,对固定模板excel表格指定内容进行替换,并生成文件,就是一个比较好的选择

实际使用中,模板excel表格中单个单元格内,可能有多个需要替换的内容,为便于使用,单独将字符串替换功能写为函数

def replace_all(string, **kwargs):
    """
    将字符串string中所有与替换字典kwargs中key相同的字符串,替换为value
    :param str string: 待替换的原始字符串
    :param dict kwargs: 需替换的旧新字符串键值对,字典
    """
    rep_string = string
    for key,value in kwargs.items():
        rep_string = rep_string.replace(key, value)
    return rep_string

python实现方法1

xlrd模块,常用于读取xls和xlsx格式excel表格
xlwt模块,常用于写入xls格式excel表格
xlutils模块,可将xlrd.Book对象转为xlwt.Workbook对象,从而实现对已有excel表格的写入功能

from xlrd import open_workbook  # 从0开始计数
import xlwt  # 从0开始计数
from xlutils.copy import copy
from time import time

def excel_replace_1():
    start_time = time()
    # 数据文件读取
    data_wb = open_workbook(r'E:\测试\数据文件.xlsx')
    data_ws = data_wb.sheet_by_index(0)
    name_list = data_ws.col_values(1)[1:]
    num_list = data_ws.col_values(2)[1:]
    # 模板文件,formatting_info=True参数不改变原样式
    template_wb = open_workbook(r'E:\测试\模板文件.xls',formatting_info=True)
    num_sheets = template_wb.nsheets  # 模板文件sheet数量
    for i in range(len(name_list)):
        copy_wb = copy(template_wb)  # 复制xlrd.workbook对象
        rep_dict = {'{姓名}': name_list[i], '{案卷号}': str(int(num_list[i]))}
        for k in range(0, num_sheets):
            copy_ws = copy_wb.get_sheet(k)
            template_ws = template_wb.sheet_by_index(k)
            for r in range(1, template_ws.nrows):
                for c in range(1, template_ws.ncols):
                    cell_value = str(template_ws.cell_value(r, c))  # 读取为字符串,否则会报错
                    if ('{房号}' in cell_value) or ('{案卷号}' in cell_value):
                        cell_value = replace_all(cell_value, **rep_dict)  # 替换内容
                        copy_ws.write(r, c, cell_value)
        # 保存文件
        copy_wb.save(r'E:\测试\输出文件\政治保卫局《{}》资料.xls'.format(rep_dict['{姓名}']))
        #break  # 仅循环一次,测试用
    end_time = time()
    print('所有文件已生成,累计用时:%.4f秒' % (end_time - start_time))

优点:速度快;缺点:单元格赋值会改变单元格格式

方法1只能生成xls格式excel表格,如果需要生成xlsx格式,参考方式2

python实现方法2

openpyxl模块,常用于读取、写入xlsx格式excel表格

from openpyxl import load_workbook  # 从1开始计数

def excel_replace_2():
    start_time = time()
    # 数据文件读取
    data_wb = load_workbook(r'E:\测试\数据文件.xlsx')
    data_ws = data_wb['Sheet1']
    name_list = data_ws['B'][1:]
    num_list = data_ws['C'][1:]
    # 遍历,写入模板文件
    for i in range(len(name_list)):
        write_wb = load_workbook(r'E:\测试\模板文件.xlsx')
        sheet_list = write_wb.sheetnames  # 模板文件sheet名称列表
        rep_dict = {'{姓名}': name_list[i].value, '{案卷号}': str(num_list[i].value)}
        for sheet in sheet_list:
            write_ws = write_wb[sheet]
            for colum in write_ws.columns:
                for cell in colum:
                    cell_value = str(cell.value)
                    if ('{姓名}' in cell_value) or ('{案卷号}' in cell_value):
                        cell_value = replace_all(cell_value, **rep_dict)
                        cell.value = cell_value
        # 保存文件
        write_wb.save(r'E:\测试\输出文件\政治保卫局《{}》资料.xlsx'.format(rep_dict['{姓名}']))
        #break  # 仅循环一次,测试用
    end_time = time()
    print('所有文件已生成,累计用时:%.4f秒' % (end_time - start_time))

优点:单元格赋值不改变格式,缺点:较方式1速度太慢

对2种方式的文件生成速度进行测试:生成1500个文件,方式1用时12秒,方式2用时98秒

python实现方法3

pywin32模块可以直接调用Excel程序,试验使用该模块实现以上功能

from win32com.client import Dispatch
excel = Dispatch('Excel.Application')
excel.Application.Visible = 0  # 后台运行,不显示界面
excel.Application.DisplayAlerts = 0  # 不显示警告信息

def excel_replace_3():
    start_time = time()
    # 数据文件
    data_wb = excel.Workbooks.Open(r'E:\测试\数据文件.xlsx')
    data_ws = data_wb.Worksheets(1)
    max_row = data_ws.UsedRange.Rows.count  # 最大行数

    for i in range(2, max_row+1):
        write_wb = excel.Workbooks.Open(r'E:\测试\模板文件.xlsx')
        sheet_count = write_wb.Sheets.count
        rep_dict = {'{姓名}': data_ws.Cells(i, 2).Value, '{案卷号}': str(int(data_ws.Cells(i, 3).Value))}
        for s in range(1, sheet_count+1):
            write_ws = write_wb.Worksheets(s)
            max_r = write_ws.UsedRange.Rows.count
            max_c = write_ws.UsedRange.Columns.count
            for r in range(1, max_r+1):
                for c in range(1, max_c+1):
                    cell_value = str(write_ws.Cells(r, c).Value)
                    if ('{姓名}' in cell_value) or ('{案卷号}' in cell_value):
                        cell_value = replace_all(cell_value, **rep_dict)
                        write_ws.Cells(r, c).Value = cell_value
        save_file = r'E:\测试\输出文件\政治保卫局《{}》资料.xlsx'.format(rep_dict['{姓名}'])
        write_wb.SaveCopyAs(save_file)  # 保存文件
        write_wb.Close()  # 关闭wb
        #break  # 仅循环一次,测试用
    data_wb.Close()
    excel.Application.Quit()
    end_time = time()
    print('所有文件已生成,累计用时:%.4f秒' % (end_time - start_time))

测试结果:由于运行太慢,只测试生成150个文件,却用时494秒

既然试验了pywin32调用Excel程序实现邮件合并功能,为什么不直接在Excel中使用VBA呢?

VBA实现方法

Sub excel_replace()
    tm = Now()
    Application.ScreenUpdating = False  '关闭屏幕更新
    Application.Visible = False  '后台运行,不显示界面
    Application.DisplayAlerts = False  '不显示警告信息
    
    Dim data_wb, write_wb As Workbook  '数据源文件,模板文件
    Dim data_ws As Worksheet  '文件中的worksheet
    Dim arr  '数组
    
    Set data_wb = Application.Workbooks.Open("E:\测试\数据文件.xlsx")   '打开数据源文件
    Set data_ws = data_wb.Worksheets(1)
    arr = data_ws.UsedRange  '所有数据行读取为数组
    
    For i = 2 To UBound(arr):
        Set write_wb = Application.Workbooks.Open("E:\测试\模板文件.xlsx")  '打开模板文件
        For Each sht In Worksheets
            With sht
                .Cells.Replace What:="{姓名}", Replacement:=arr(i, 2)   '替换内容
                .Cells.Replace What:="{案卷号}", Replacement:=arr(i, 3)
            End With
        Next
        save_file = "E:\测试\输出文件\政治保卫局《" & arr(i, 2) & "》资料.xlsx"
        write_wb.SaveCopyAs filename:=save_file
        write_wb.Close (False)
        'Exit For '强制退出for循环,单次测试使用
    Next
    data_wb.Close (False)
    
    Application.ScreenUpdating = True  '打开
    Application.Visible = True
    Application.DisplayAlerts = True
    Debug.Print ("所有文件已生成,累计用时:" & Format(Now() - tm, "hh:mm:ss"))  '耗时

End Sub

测试结果:生成1500个文件,用时338秒

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔_51

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值