虽然一般自动化持续集成中都用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()