python|python群发邮件

1. 前言
1.1 应朋友要求,写一个群发邮件的脚本,用来实现往每个人的邮箱里边发送自己的工资条
2. 数据格式,最后一列是邮箱地址
python|python群发邮件
文章图片

3. 脚本实现的功能
3.1 自定义邮件标题
3.2 记录发送成功或失败的个数,防止发送失败
4. 代码实现

# -*- coding: utf-8 -*- # @Time : 2021/3/26 10:11 # @Author : liyf--95/02/02 # @File : send_email.py # @Software: PyCharmimport xlrd import time import re from email.mime.text import MIMEText from smtplib import SMTP_SSLfrom loguru import logger# qq邮箱smtp服务器 host_server = 'smtp.qq.com' # sender_qq为发件人的qq号码 sender_qq = '123456@qq.com' # 第三方客户端登录时需要的授权码,不是qq密码 pwd = 'xxxxxxxxxxxxx' # 发件人的邮箱 sender_qq_mail = '123456@qq.com' # 获取当前月份 batch = time.strftime("%Y-%m", time.localtime())suffix = time.strftime("%Y%m", time.localtime())def get_success_error_counts(): """ 用来读取日志文件中的数据,并转成列表形式,方便调用该函数处理列表中的数据,用来做去重处理 :return: 列表 """ success_email_list = []try: with open(f'success_log_{suffix}.txt', 'r', encoding='utf8') as f: results = f.readlines() for res in results: success_email_list.append(res.strip()) except Exception: logger.error(f'success_log_{suffix}.txt 文件不存在,初始化列表为0!') success_email_list = success_email_listerror_list = [] try: with open(f'error_log_{suffix}.txt', 'r', encoding='utf8') as f: results = f.readlines() for res in results: restr = res.strip() email = re.findall(re.compile(r"email': '(.*?)', '", re.S), restr)[0] error_list.append(email) except Exception: error_list = error_listreturn success_email_list, error_listdef read_excel(subject): """ 读取excel数据 :param subject: 自定义的邮件标题 :return: """ workbook = xlrd.open_workbook('工资条2.xlsx')worksheet = workbook.sheet_by_index(0)nrows = worksheet.nrows # 定义一个空列表,用来存放每一个员工的数据,包括表头 total_list = [] for i in range(nrows): data_list = worksheet.row_values(i) if data_list[0] == '': pass else: total_list.append(data_list) logger.info(f'数据读取完毕,共有 {len(total_list) - 1} 位同事') logger.info('------------------------------------------------------------') time.sleep(2) for k, v in enumerate(total_list[1:]): msg_content = '' for i, j in enumerate(v): if total_list[0][i] == '邮箱': pass else: # 有一些列的数据为空,处理数据 val = '无' if str(v[i]).strip() == '' else v[i] msg = f'#{total_list[0][i]}:{val}\n' msg_content += msg name = v[0] email_addr = v[-1] success, error = get_success_error_counts() logger.info(f'正在向第 {k + 1}/{len(total_list) - 1} 位同事 {name} 发送邮件,请稍等...') if len(success) == 0: # 说明日志中没有数据,即还没有发送成功的例子 # 开始发送数据 email_content = f'尊敬的 {name} 同事,您好,您的 {batch} 月份工资单信息如下:\n{msg_content}#发送时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}' send_email_to_member(email_addr, email_content, name, subject) logger.info(f'已发送至邮箱:{email_addr},接收人:{name}') else: # 已经有发送成功的例子,并已存入success_log.txt文件中 email_list = []# 用来存放success_log.txt文件中的邮箱地址,用来去重 for data in success: email_list.append(str(data).split('---->')[-1]) if email_addr in email_list: # 如果需要发送邮件的邮箱地址在success_log.txt文件中,则说明该邮箱已经发送过,无需重复发送 logger.warning(f'{name} 同事:{email_addr} 已经发送过了,无需重复发送邮件!!') pass else: # 正常发送邮件 email_content = f'尊敬的 {name} 同事,你好,您的 {batch} 月份工资单信息如下:\n{msg_content}#发送时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}' send_email_to_member(email_addr, email_content, name, subject) logger.info(f'已发送至邮箱:{email_addr},接收人:{name}') logger.info('------------------------------------------------------------') logger.info(f'脚本运行结束!等待5分钟后自动关闭窗口(或者手动点击窗口右上角关闭)!!!') logger.info('运行结果:') logger.info(f'{len(total_list) - 1} 位同事已全部发送完毕') success, error = get_success_error_counts() logger.info(f'Successd:{len(success)},Failed:{len(error)}') time.sleep(300)def send_email_to_member(email_addr, email_content, name, subject): """ 发送邮件 :param email_addr: 收件人邮箱地址 :param email_content: 需要发送的正文内容 :param name: 收件人姓名 :return: """ # ssl登录 smtp = SMTP_SSL(host_server) # set_debuglevel()是用来调试的。参数值为1表示开启调试模式,参数值为0关闭调试模式, smtp.set_debuglevel(0) smtp.ehlo(host_server) smtp.login(sender_qq, pwd)msg = MIMEText(email_content, "plain", 'utf-8') msg["Subject"] = subject# 邮件标题 msg["From"] = sender_qq_mail# 发件人 msg["To"] = email_addr# 收件人邮箱 try: smtp.sendmail(sender_qq_mail, email_addr, msg.as_string()) smtp.quit() msg = f'{name} 发送成功,时间: {time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}---->{email_addr}' with open(f'success_log_{suffix}.txt', 'a', encoding='utf8') as f: f.write(msg) f.write('\n') f.close() time.sleep(0.5) except Exception as e: logger.error(f'发送失败--->{name}\n原因:{e}') item = {} item['name'] = name item['email'] = email_addr item['error_reason'] = e item['date'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) with open(f'error_log_{suffix}.txt', 'a', encoding='utf8') as f: f.write(str(item)) f.write('\n') f.close()if __name__ == '__main__': subject = input('请输入自定义邮件标题:') logger.info(f'自定义邮件标题为:{subject}') read_excel(subject)

5. 逻辑梳理
5.1 该脚本的使用qq邮箱发送,其中获取授权码可以 点击这里参考博客
5.2 注意事项
5.2.1 excel名称必须为 `工资条2.xlsx`
5.2.2 `邮箱` 列必须为最后一列,`姓名` 列必须在第一列,因为代码中 `name=v[0], email_addr=v[-1]` 是固定的。可以自己做适当修改
5.2.3 表头名称可以随意改动,列数也可以随意增减,但要保证 `邮箱` 和 `姓名` 列存在
6. success_log_202103.txt 和 error_log_202103.txt 的作用
6.1 success_log_202103.txt
6.1.1 用来记录发送成功的数据
【python|python群发邮件】6.1.2 发送邮件之前,会先读取该txt文件,并判断要发送的email地址是否在txt里边,如果存在,则不发送,防止重复发送
6.2 error_log_202103.txt
6.2.1 一般情况下,没有这个文件,但是由于一些不可控因素,比如邮箱地址不存在或者断网等,会导致发送邮件失败
6.2.2 发送失败之后会把当前发送的数据记录下来,就会生成这个文件
6.2.3 如果该文件有数据,则首先检查是否是邮箱不正确导致的,如果不是,重新运行exe文件
7. 测试
7.1 使用 `pyinstaller -F send_email.py` 打包 py 文件为 exe 可执行文件
7.2 运行截图

python|python群发邮件
文章图片

7.3 邮件内容

python|python群发邮件
文章图片


    推荐阅读