Python3.x 发送各种形式的告警邮件内容
在写脚本时,放到后台运行,想知道执行情况,会通过邮件、短信、微信等方式通知管理员,邮件目前用的最多的通知方式。在linux下,Shell脚本发送邮件告警是件很简单的事,有现成的邮件服务软件或者调用运营商邮箱服务器,对于Python来说,需要编写脚本调用邮件服务器来发送邮件。
Python分别提供了收发邮件的库,smtplib、poplib和imaplib。
本文主要学习如何使用smtplib库发送各种形式的邮件内容。该库主要用smtplib.SMTP()类,使用这个类封装一个SMTP连接,语法如下:
smtplib.SMTP(host ='',port = 0,local_hostname = None,[ timeout,] source_address = None )SMTP实例后有以下常用方法:
方法 | 描述 |
SMTP.connect([host[, port]]) | 连接到指定的SMTP服务器 |
SMTP.login(user, password) | 登录SMTP服务器 |
SMTP.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[]) | 发送邮件 from_addr:邮件发件人 to_addrs:邮件收件人 msg:发送消息 |
SMTP.quit() | 关闭SMTP会话 |
SMTP.close() | 关闭SMTP服务器连接 |
为更好学习smtplib模块,我们下面写几个具体的示例来熟悉它的用法。
示例1:发送文本邮件
先通过本地SMTP服务器发送邮件。
本地SMTP服务器,可以通过安装sendmail、postfix等服务提供。
例如安装sendmail:
yum install sendmail –ysystemctl start sendmail
smtp默认25端口,查看是否监听:
ss -anpt|grep 25sendmail服务运行,就可以使用了:
from smtplib import SMTPs = SMTP("localhost")to_mail = ["baojingtongzhi@163.com"] #收件人列表,可以写多个# 内容必须靠左,否则邮件头不识别msg = '''\From: aliang@ctnrs.comSubject: test这是一份测试邮件'''.encode("utf8")s.sendmail("aliang@ctnrs.com",to_mail,msg)s.quit()
msg对象里From表示发件人,Subject是邮件标题,换行后输入的是邮件内容。

上面示例使用本地SMTP服务器发送的邮件,这种自建的SMTP服务器容易被当做垃圾邮件,甚至大部分公有云也不允许自建(禁止25端口),所以一般情况下都采用个人邮箱或者企业邮箱发邮件。
例如,使用163邮箱发邮件:
from smtplib import SMTPemail_account = "baojingtongzhi@163.com"account_password = "VCGXTBHXFPVDVLGZ" #授权密码s = SMTP("smtp.163.com")s.login(email_account, account_password)to_mail = ["1121267855@qq.com"]msg = f'''\From: {email_account}Subject: 测试这是一封测试邮件'''.encode("utf8")s.sendmail("baojingtongzhi@163.com",to_mail,msg)s.quit()
现在一般个人邮箱需要授权码才可以在客户端收发邮件,设置-> POP3/SMTP/IMAP->授权密码:

运行结果:
smtplib.SMTPDataError: (554, b'DT:SPM 163 smtp12,EMCowAC312M29l5fNpBQAw--.41693S2 1600058934,please see http://mail.163.com/help/help_spam_16.htm?ip=27.189.225.231&hostid=smtp12&time=1600058934')访问给出的163网址,SMTP554错误是:"554 DT:SUM 信封发件人和信头发件人不匹配"
在上面第一个示例中你会发现收件人是空的,看这样163的SMTP服务器拒绝的原因应该就是这里收件人没指定。
重新修改下msg对象,添加收件人:
msg = f'''\From: {email_account}To: {','.join(to_mail)}Subject:测试这是一封测试邮件'''.encode("utf8")
','.join(to_mail)转成字符串,以逗号分隔元素。
如果msg对象含带中文需要编码
encode("utf8")

可以正常发送邮件了。msg这个格式是SMTP规定的,一定要遵守。
示例2:发送邮件并抄送
import smtplibdef sendMail(body):smtp_server = 'smtp.163.com'email_account = 'baojingtongzhi@163.com'account_password = 'VCGXTBHXFPVDVLGZ'to_mail = ["1121267855@qq.com"]cc_mail = ["xxx@163.com"]from_name = 'monitor'subject = '监控'"""msg = '''\From: {email_account}To: {','.join(to_mail)}Subject: 测试'''"""# 或者写成列表再拼接,相比上面顶头写更美化些mail = ["From: %s <%s>" % (from_name, account_password),"To: %s" % ','.join(to_mail),"Subject: %s" % subject,"Cc: %s" % ','.join(cc_mail),"",body]msg = '\n'.join(mail).encode("utf8")try:s = smtplib.SMTP()s.connect(smtp_server, '25')s.login(email_account, account_password)s.sendmail(email_account, to_mail+cc_mail, msg)s.quit()except smtplib.SMTPException as e:print("Error: %s" %e)if __name__ == "__main__":sendMail("这是一封测试邮件")

s.sendmail(from_mail,to_mail+cc_mail, msg) 这里看到,收件人和抄送人为什么放一起发送呢?
其实无论是收件人还是抄送人,它们收到的邮件都是一样的,SMTP服务器也是认为发件人这样一封一封的发出。所以实际上并没有抄送这个概念,只是在邮件头加了抄送人的信息罢了!
示例3:发送邮件带附件
由于SMTP.sendmail()方法不支持添加附件,所以需要借助email模块来实现。email模块是一个构造邮件和解析邮件的模块。
import smtplibfrom email.mime.text import MIMETextfrom email.mime.multipart import MIMEMultipartfrom email.header import Headerfrom email import encodersfrom email.mime.base import MIMEBasefrom email.utils import parseaddr, formataddr# 格式化邮件地址def formatAddr(s):name, addr = parseaddr(s)return formataddr((Header(name, 'utf-8').encode(), addr))def sendMail(body, attachment):smtp_server = 'smtp.163.com'email_account = 'baojingtongzhi@163.com'account_password = 'VCGXTBHXFPVDVLGZ'to_mail = ["1121267855@qq.com"]# 构造一个MIMEMultipart对象代表邮件本身msg = MIMEMultipart()# Header对中文进行转码msg['From'] = formatAddr('管理员 <%s>' % email_account)msg['To'] = ','.join(to_mail)msg['Subject'] = Header('监控', 'utf-8')# plain代表纯文本msg.attach(MIMEText(body, 'plain', 'utf-8'))# 二进制方式模式文件with open(attachment, 'rb') as f:# MIMEBase表示附件的对象mime = MIMEBase('text', 'txt', filename=attachment) # 使用MIMEBase类构造附件并添加到msg对象# filename是显示附件名字mime.add_header('Content-Disposition', 'attachment', filename=attachment)# 获取附件内容mime.set_payload(f.read())encoders.encode_base64(mime)# 作为附件添加到邮件msg.attach(mime)try:s = smtplib.SMTP()s.connect(smtp_server, "25")s.login(email_account, account_password)s.sendmail(email_account, to_mail, msg.as_string()) # as_string()把MIMEText对象变成strs.quit()except smtplib.SMTPException as e:print("Error: %s" % e)if __name__ == "__main__":sendMail('这是一封携带附件的测试邮件', 'test.txt')

示例4:发送HTML邮件
import smtplibfrom email.mime.text import MIMETextfrom email.mime.multipart import MIMEMultipartfrom email.header import Headerfrom email.utils import parseaddr, formataddr# 格式化邮件地址def formatAddr(s):name, addr = parseaddr(s)return formataddr((Header(name, 'utf-8').encode(), addr))def sendMail(body):smtp_server = 'smtp.163.com'email_account = 'baojingtongzhi@163.com'account_password = 'VCGXTBHXFPVDVLGZ'to_mail = ["1121267855@qq.com"]# 构造一个MIMEMultipart对象代表邮件本身msg = MIMEMultipart()# Header对中文进行转码msg['From'] = formatAddr('管理员 <%s>' % email_account)msg['To'] = ','.join(to_mail)msg['Subject'] = Header('监控', 'utf-8')msg.attach(MIMEText(body, 'html', 'utf-8'))try:s = smtplib.SMTP()s.connect(smtp_server, "25")s.login(email_account, account_password)s.sendmail(email_account, to_mail, msg.as_string()) # as_string()把MIMEText对象变成strs.quit()except smtplib.SMTPException as e:print("Error: %s" % e)if __name__ == "__main__":body = """测试邮件
这是一封HTML测试邮件
"""sendMail(body)

示例5:发送图片邮件
import smtplibfrom email.mime.text import MIMETextfrom email.mime.image import MIMEImagefrom email.mime.multipart import MIMEMultipartfrom email.header import Headerfrom email.utils import parseaddr, formataddr# 格式化邮件地址def formatAddr(s):name, addr = parseaddr(s)return formataddr((Header(name, 'utf-8').encode(), addr))def sendMail(body, image):smtp_server = 'smtp.163.com'email_account = 'baojingtongzhi@163.com'account_password = 'VCGXTBHXFPVDVLGZ'to_mail = ["1121267855@qq.com"]# 构造一个MIMEMultipart对象代表邮件本身msg = MIMEMultipart()# Header对中文进行转码msg['From'] = formatAddr('管理员 <%s>' % email_account)msg['To'] = ','.join(to_mail)msg['Subject'] = Header('监控', 'utf-8')msg.attach(MIMEText(body, 'html', 'utf-8'))# 二进制模式读取图片with open(image, 'rb') as f:msgImage = MIMEImage(f.read()) # 使用MIMEImage类构造图片并添加到msg对象# 定义图片ID,根据ID在HTML里获取图片msgImage.add_header('Content-ID', '') msg.attach(msgImage)try:s = smtplib.SMTP()s.connect(smtp_server, "25")s.login(email_account, account_password)s.sendmail(email_account, to_mail, msg.as_string()) # as_string()把MIMEText对象变成strs.quit()except smtplib.SMTPException as e:print("Error: %s" % e)if __name__ == "__main__":body = """测试图片
"""sendMail(body, "123.jpg")

动手试试吧~ 代码复制粘贴就行。
- END -
推荐阅读 Python 自动创建 Grafana 仪表板 Python 调用 Kubernetes API 自动化管理资源 支付宝架构师眼中的高并发架构 SpringCloud微服务项目运维必知必会 Java 应用最常见的3个问题排查思路 一篇漫画带你了解 Linux 内核长啥样!
点亮,服务器三年不宕机

