虽然一般自动化持续集成中都用Jenkins来发送邮件,但了解掌握一下python的smtplib模块发送邮件也是必要的。
先明确一下发邮件的步骤:
1.确定邮件服务商:网易、qq等
2.登录邮箱:用户名/密码(密码一般是授权码)
3.编辑邮件主题、内容、附件
4.发送邮件
最简单的实现:
server = smtplib.SMTP('smtp.163.com',25) server.login(emailname,emailpwd) # 登录 # 邮件内容 msg = '''\From: xia Subject: Hello World This is a test ''' # 发送邮件 server.sendmail(emailname,'18551052425@163.com',msg)
如上代码创建一个实例化对象server,调用SMTP类下的login方法,采用163邮件服务,默认端口是25,emailname和emailpwd分别为发送邮件的用户名和授权码。
实际在发邮件的时候,我们通常使用加密模式。简单的两种加密方法:
1.tls模式加密
server = smtplib.SMTP('smtp.163.com',25) server.login(emailname,emailpwd) # 登录 context = ssl.create_default_context() server.starttls(context=context) # 邮件内容 msg = '''\ From: xia Subject: Hello World This is a test ''' # 发送邮件 server.sendmail(emailname,'18551052425@163.com',msg) server.quit()
2.ssl加密,默认加密端口是465
server = smtplib.SMTP_SSL('smtp.163.com',465) server.login(emailname,emailpwd) # 登录 # 邮件内容 msg = '''\ From: xia Subject: Hello World This is a test ''' # 发送邮件 server.sendmail(emailname,'18551052425@163.com',msg) server.quit()
创建server后,要记得quit关闭。也可以使用上下文管理器,防止忘记关闭邮件:
with smtplib.SMTP_SSL('smtp.163.com',465) as server: server.login(emailname,emailpwd) # 登录 # 邮件内容 msg = '''\ From: xia Subject: Hello World This is a test ''' # 发送邮件 server.sendmail(emailname,'18551052425@163.com',msg)
但是实际应用中,不可能会发送这么简单的邮件。我们需要丰富邮件主题、内容和附件。
这就需要引入3个模块:
from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.application import MIMEApplication
首先创建一个带附件的实例MIMEMultipart(),设置邮件主题,from,to信息:
msg_total = MIMEMultipart() msg_total['subject'] = Header(self.subject,'utf-8') msg_total['From'] = emailname msg_total['To'] = tolist
添加邮件正文内容,这里添加html格式的内容:
content = '''<p style="color:red">你好,我是一份测试邮件。</p>''' msg_total.attach(MIMEText(content, 'html','utf-8'))
也可以读取某个文件内容作为邮件正文:
with open(file, 'rb') as f: content = f.read() msg_total.attach(MIMEText(content, 'html', 'utf-8'))
添加附件的话,MIMEText模块和MIMEAplication模块都可以添加,只不过看源码发现,MIMEApplication比MIMEText要简单一些,需要配置的参数少一些。
MIMEText模块添加附件:
with open(file, 'rb') as f: mail_attach = f.read() att = MIMEText(mail_attach, 'base64', 'utf-8') att["Content-Type"] = 'application/octet-stream' # 这里的filename可以任意写,写什么名字,邮件中显示什么名字 att["Content-Disposition"] = 'attachment; filename="测试报告"' msg_total.attach(att)
MIMEApplication模块添加附件:
with open(file_new, 'rb') as f: mail_attach = f.read() att2 = MIMEApplication(mail_attach) # 添加附件的头信息 att2.add_header('content-disposition', 'attachment', filename='{}'.format(file_name)) msg_total.attach(att2)
对比代码也可以发现,MIMEapplication模块添加附件更方便一些。
上面邮件正文和附件文件都是写死的,一般在应用中,我们可以获取当前最新的测试执行报告作为附件:
file = os.listdir(report_path) # 列出目录的下所有文件和文件夹 file.sort(key=lambda fn: os.path.getmtime(report_path + "\" + fn)) # 按时间排序 file_name = file[-1] file_new = os.path.join(report_path, file[-1]) # 获取最新的文件保存到file_new # 添加文件内容作为正文模块 with open(file_new, 'rb') as f: content = f.read() msg_total.attach(MIMEText(content, 'html', 'utf-8')) # 附件模块 with open(file_new, 'rb') as f: mail_attach = f.read() att = MIMEText(mail_attach, 'base64', 'utf-8') att["Content-Type"] = 'application/octet-stream' # 这里的filename可以任意写,写什么名字,邮件中显示什么名字 att["Content-Disposition"] = 'attachment; filename="{}"'.format(file_name) msg_total.attach(att)
整合以上邮件主题、内容、附件,加上异常处理,并封装成一个类,便于后续调用。完整代码如下:
import smtplib from python15.class_email.conf import * from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.application import MIMEApplication from email.header import Header import os class MyEmail: def __init__(self,host,port,tolist,subject,content=None): self.host = host #邮件服务地址 self.port = port # 邮件服务端口 self.tolist = tolist # 接收人 self.subject = subject # 邮件主题 self.content = content # 邮件内容 def send_email(self): # 总的邮件内容,创建一个带附件的实例 msg_total = MIMEMultipart() msg_total['subject'] = Header(self.subject,'utf-8') msg_total['From'] = emailname msg_total['To'] = self.tolist # 添加普通正文模块 # msg_total.attach(MIMEText(self.content, 'html','utf-8')) file = os.listdir(report_path) # 列出目录的下所有文件和文件夹 file.sort(key=lambda fn: os.path.getmtime(report_path + "\" + fn)) # 按时间排序 file_name = file[-1] file_new = os.path.join(report_path, file[-1]) # 获取最新的文件保存到file_new # 添加文件内容作为正文模块 with open(file_new, 'rb') as f: content = f.read() msg_total.attach(MIMEText(content, 'html', 'utf-8')) # 附件模块1 MIMEText添加附件 with open(file_new, 'rb') as f: mail_attach = f.read() att1 = MIMEText(mail_attach, 'base64', 'utf-8') att1["Content-Type"] = 'application/octet-stream' # 这里的filename可以任意写,写什么名字,邮件中显示什么名字 att1["Content-Disposition"] = 'attachment; filename="{}"'.format(file_name) msg_total.attach(att1) # 附件模块2 MIMEApplication模块添加附件 with open(file_new, 'rb') as f: mail_attach = f.read() att2 = MIMEApplication(mail_attach) # 添加附件的头信息 att2.add_header('content-disposition', 'attachment', filename='{}'.format(file_name)) msg_total.attach(att2) try: self.server = smtplib.SMTP_SSL(self.host, self.port) self.server.login(emailname, emailpwd) self.server.sendmail(emailname, self.tolist, msg_total.as_string()) print("邮件发送成功") except smtplib.SMTPException as e: print("邮件发送失败",e) finally: self.server.quit()