在写脚本时,放到后台运行,想知道执行情况,会通过邮件、SMS(短信)、飞信、微信等方式通知管理员,用的最多的是邮件。在linux下,Shell脚本发送邮件告警是件很简单的事,有现成的邮件服务软件或者调用运营商邮箱服务器。

对于Python来说,需要编写脚本调用邮件服务器来发送邮件,使用的协议是SMTP。接收邮件,使用的协议是POP3和IMAP。我想有必要说明下 ,POP3和IMAP的区别:POP3在客户端邮箱中所做的操作不会反馈到邮箱服务器,比如删除一封邮件,邮箱服务器并不会删除。IMAP则会反馈到邮箱服务器,会做相应的操作。

Python分别提供了收发邮件的库,smtplib、poplib和imaplib。

本章主要讲解如果使用smtplib库实现发送各种形式的邮件内容。在smtplib库中,主要主要用smtplib.SMTP()类,用于连接SMTP服务器,发送邮件。

这个类有几个常用的方法:

方法

描述

SMTP.set_debuglevel(level) 设置输出debug调试信息,默认不输出
SMTP.docmd(cmd[, argstring]) 发送一个命令到SMTP服务器
SMTP.connect([host[, port]]) 连接到指定的SMTP服务器
SMTP.helo([hostname]) 使用helo指令向SMTP服务器确认你的身份
SMTP.ehlo(hostname) 使用ehlo指令像ESMTP(SMTP扩展)确认你的身份
SMTP.ehlo_or_helo_if_needed() 如果在以前的会话连接中没有提供ehlo或者helo指令,这个方法会调用ehlo()或helo()
SMTP.has_extn(name) 判断指定名称是否在SMTP服务器上
SMTP.verify(address) 判断邮件地址是否在SMTP服务器上
SMTP.starttls([keyfile[, certfile]]) 使SMTP连接运行在TLS模式,所有的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服务器连接

看下官方给的示例:

>>> import smtplib
>>> s=smtplib.SMTP("localhost")
>>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
>>> msg = '''\
... From: Me@my.org
... Subject: testin'...
...
... This is a test '''
>>> s.sendmail("me@my.org",tolist,msg)
{ "three@three.org" : ( 550 ,"User unknown" ) }
>>> s.quit()

我们根据示例给自己发一个邮件测试下:

我这里测试使用本地的SMTP服务器,也就是要装一个支持SMTP协议的服务,比如sendmail、postfix等。

CentOS安装sendmail:yum install sendmail

>>> import smtplib
>>> s = smtplib.SMTP("localhost")
>>> tolist = ["xxx@qq.com", "xxx@163.com"]
>>> msg = '''\
... From: Me@my.org
... Subject: test
... This is a test '''
>>> s.sendmail("me@my.org", tolist, msg)
{}

进入腾讯和网易收件人邮箱,就能看到刚发的测试邮件,一般都被邮箱服务器过滤成垃圾邮件,所以收件箱没有,你要去垃圾箱看看。

可以看到,多个收件人可以放到一个列表中进行群发。msg对象里From表示发件人,Subject是邮件标题,换行后输入的是邮件内容。

上面是使用本地SMTP服务器发送的邮件,测试下用163服务器发送邮件看看效果:

>>> import smtplib
>>> s = smtplib.SMTP("smtp.163.com")
>>> s.login("baojingtongzhi@163.com", "xxx")
(235, 'Authentication successful')
>>> tolist = ["xxx@qq.com", "xxx@163.com"]
>>> msg = '''\
... From: baojingtongzhi@163.com
... Subject: test
... This is a test '''
>>> s.sendmail("baojingtongzhi@163.com", tolist, msg)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python2.6/smtplib.py", line 725, in sendmail
raise SMTPDataError(code, resp)
smtplib.SMTPDataError: (554, 'DT:SPM 163 smtp10,DsCowAAXIdDIJAtYkZiTAA--.65425S2 1477125592,please see http://mail.163.com/help/help_spam_16.htm?ip=119.57.73.67&hostid=smtp10&time=1477125592')

访问给出的163网址,SMTP554错误是: "554 DT:SUM 信封发件人和信头发件人不匹配;"

大概已经明白啥意思,看上面再使用本地SMTP服务器时候,收件人位置是“undisclosed-recipients”,看这样163的SMTP服务器不给我们服务的原因就是这里收件人没指定。

重新修改下msg对象,添加上收件人:

>>> msg = '''\
... From: baojingtongzhi@163.com
... To: 962510244@qq.com ,xxx@163.com
... Subject: test
...
... This is a test '''
>>> s.sendmail("baojingtongzhi@163.com", tolist, msg)
{}
 

好了,可以正常发送邮件了。msg这个格式是SMTP规定的,一定要遵守。

1.1 Python发送邮件并抄送

#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
def sendMail(body):
smtp_server = 'smtp.163.com'
from_mail = 'baojingtongzhi@163.com'
mail_pass = 'xxx'
to_mail = ['xxx@qq.com', 'xxx@163.com']
cc_mail = ['lizhenliang@xxx.com']
from_name = 'monitor'
subject = u'监控'.encode('gbk') # 以gbk编码发送,一般邮件客户端都能识别
# msg = '''\
# From: %s <%s>
# To: %s
# Subject: %s
# %s''' %(from_name, from_mail, to_mail_str, subject, body) # 这种方式必须将邮件头信息靠左,也就是每行开头不能用空格,否则报SMTP 554
mail = [
"From: %s <%s>" % (from_name, from_mail),
"To: %s" % ','.join(to_mail), # 转成字符串,以逗号分隔元素
"Subject: %s" % subject,
"Cc: %s" % ','.join(cc_mail),
"",
body
]
msg = '\n'.join(mail) # 这种方式先将头信息放到列表中,然后用join拼接,并以换行符分隔元素,结果就是和上面注释一样了
try:
s = smtplib.SMTP()
s.connect(smtp_server, '')
s.login(from_mail, mail_pass)
s.sendmail(from_mail, to_mail+cc_mail, msg)
s.quit()
except smtplib.SMTPException as e:
print "Error: %s" %e
if __name__ == "__main__":
sendMail("This is a test!")

s.sendmail(from_mail, to_mail+cc_mail, msg) 在这里注意下,收件人和抄送人为什么放一起发送呢?其实无论是收件人还是抄送人,它们收到的邮件都是一样的,SMTP都是认为收件人这样一封一封的发出。所以实际上并没有抄送这个概念,只是在邮件头加了抄送人的信息罢了!另外,如果不需要抄送人,直接把上面cc的信息去掉即可。

1.2 Python发送邮件带附件

由于SMTP.sendmail()方法不支持添加附件,所以可以使用email模块来满足需求。email模块是一个构造邮件和解析邮件的模块。

先看下如何用email库构造一个简单的邮件:

message = Message()
message['Subject'] = '邮件主题'
message['From'] = from_mail
message['To'] = to_mail
message['Cc'] = cc_mail
message.set_payload('邮件内容')

基本的格式就是这样的!

继续回到主题,发送邮件带附件:

#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email import encoders
from email.mime.base import MIMEBase
from 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'
from_mail = 'baojingtongzhi@163.com'
mail_pass = 'xxx'
to_mail = ['xxx@qq.com', 'xxx@163.com']
# 构造一个MIMEMultipart对象代表邮件本身
msg = MIMEMultipart()
# Header对中文进行转码
msg['From'] = formatAddr('管理员 <%s>' % from_mail).encode()
msg['To'] = ','.join(to_mail)
msg['Subject'] = Header('监控', 'utf-8').encode()
# plain代表纯文本
msg.attach(MIMEText(body, 'plain', 'utf-8'))
# 二进制方式模式文件
with open(attachment, 'rb') as f:
# MIMEBase表示附件的对象
mime = MIMEBase('text', 'txt', filename=attachment)
# 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, "")
s.login(from_mail, mail_pass)
s.sendmail(from_mail, to_mail, msg.as_string()) # as_string()把MIMEText对象变成str
s.quit()
except smtplib.SMTPException as e:
print "Error: %s" % e
if __name__ == "__main__":
sendMail('附件是测试数据, 请查收!', 'test.txt')

博客地址:http://lizhenliang.blog.51cto.com

QQ群:323779636(Shell/Python运维开发群)

1.3 Python发送HTML邮件

#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from 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'
from_mail = 'baojingtongzhi@163.com'
mail_pass = 'xxx'
to_mail = ['xxx@qq.com', 'xxx@163.com']
# 构造一个MIMEMultipart对象代表邮件本身
msg = MIMEMultipart()
# Header对中文进行转码
msg['From'] = formatAddr('管理员 <%s>' % from_mail).encode()
msg['To'] = ','.join(to_mail)
msg['Subject'] = Header('监控', 'utf-8').encode()
msg.attach(MIMEText(body, 'html', 'utf-8'))
try:
s = smtplib.SMTP()
s.connect(smtp_server, "")
s.login(from_mail, mail_pass)
s.sendmail(from_mail, to_mail, msg.as_string()) # as_string()把MIMEText对象变成str
s.quit()
except smtplib.SMTPException as e:
print "Error: %s" % e
if __name__ == "__main__":
body = """
<h1>测试邮件</h1>
<h2 style="color:red">This is a test</h1>
"""
sendMail(body)

1.4 Python发送图片邮件

#!/usr/bin/python
# -*- coding: utf-8 -*-
import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.header import Header
from 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'
from_mail = 'baojingtongzhi@163.com'
mail_pass = 'xxx'
to_mail = ['xxx@qq.com', 'xxx@163.com']
# 构造一个MIMEMultipart对象代表邮件本身
msg = MIMEMultipart()
# Header对中文进行转码
msg['From'] = formatAddr('管理员 <%s>' % from_mail).encode()
msg['To'] = ','.join(to_mail)
msg['Subject'] = Header('监控', 'utf-8').encode()
msg.attach(MIMEText(body, 'html', 'utf-8'))
# 二进制模式读取图片
with open(image, 'rb') as f:
msgImage = MIMEImage(f.read())
# 定义图片ID
msgImage.add_header('Content-ID', '<image1>')
msg.attach(msgImage)
try:
s = smtplib.SMTP()
s.connect(smtp_server, "")
s.login(from_mail, mail_pass)
s.sendmail(from_mail, to_mail, msg.as_string()) # as_string()把MIMEText对象变成str
s.quit()
except smtplib.SMTPException as e:
print "Error: %s" % e
if __name__ == "__main__":
body = """
<h1>测试图片</h1>
<img src="cid:image1"/> # 引用图片
"""
sendMail(body, 'test.png')

上面发邮件的几种常见的发邮件方法基本满足日常需求了。

原文地址:http://lizhenliang.blog.51cto.com/7876557/1875330

PS.以上均转载未测试,以下是亲测无误的代码(Python3.6版本测)

包括发送邮件,抄送邮件多人功能

    import smtplib
from email.mime.text import MIMEText
from email.header import Header
mail = Mail(app) sender = 'kakarrot2009@163.com'
subject = '我是测试的'
smtpserver = 'smtp.163.com'
username = 'kakarrot2009@163.com'
password = '************'
tolist = ["xxxxxxxx@qq.com", "yyyyyyy@qq.com"]#接收者
tocc=["mmmmmm@qq.com", "nnnnnn@qq.com"]#抄送者
msg = MIMEText('你好', 'plain', 'utf-8') # 中文需参数‘utf-8',单字节字符不需要
msg['Subject'] = Header(subject, 'utf-8')#邮件中显示内容
msg['From']='卡卡罗特<kakarrot2009@163.com>' #邮件中显示内容
msg['To']='zoro<xxxxxxxx@qq.com>;nami<yyyyyyy@qq.com>'#邮件中显示内容
msg['CC']='sanji<mmmmmm@qq.com>;luffy<nnnnnn@qq.com>'#邮件中显示内容
smtp = smtplib.SMTP()
smtp.connect('smtp.163.com')
smtp.login(username, password)
smtp.sendmail(sender,tolist+tocc, msg.as_string().encode())
smtp.quit()

【转】【Python】Python发送邮件(常见四种邮件内容)的更多相关文章

  1. Python发送邮件(常见四种邮件内容)

    Python发送邮件(常见四种邮件内容) 转载 2017年03月03日 17:17:04   转自:http://lizhenliang.blog.51cto.com/7876557/1875330 ...

  2. python接口自动化(三十二)--Python发送邮件(常见四种邮件内容)番外篇——上(详解)

    简介 本篇文章与前边没有多大关联,就是对前边有关发邮件的总结和梳理.在写脚本时,放到后台运行,想知道执行情况,会通过邮件.SMS(短信).飞信.微信等方式通知管理员,用的最多的是邮件.在linux下, ...

  3. Python遍历List集合四种方法

    这篇文章主要介绍了Python 列表(List) 的四种遍历方法实例 详解的相关资料,需要的朋友可以参考下 分别是:直接遍历对象 通过索引遍历 通过enumerate方法 通过iter方法. 使用Py ...

  4. 横向对比分析Python解析XML的四种方式

    横向对比分析Python解析XML的四种方式 在最初学习PYTHON的时候,只知道有DOM和SAX两种解析方法,但是其效率都不够理想,由于需要处理的文件数量太大,这两种方式耗时太高无法接受. 在网络搜 ...

  5. Python请求外部POST请求,常见四种请求体

    原文http://blog.csdn.net/silencemylove/article/details/50462206 HTTP 协议规定 POST 提交的数据必须放在消息主体(entity-bo ...

  6. Python实现接口测试中的常见四种Post请求数据

    前情: 在日常的接口测试工作中,模拟接口请求通常有两种方法, 利用工具来模拟,比如fiddler,postman,poster,soapUI等 利用代码来模拟,使用到一些网络模块,比如HttpClie ...

  7. Python实现二叉树的四种遍历

    对于一个没学过数据结构这门课程的编程菜鸟来说,自己能理解数据结构中的相关概念,但是自己动手通过Python,C++来实现它们却总感觉有些吃力.递归,指针,类这些知识点感觉自己应用的不够灵活,这是自己以 ...

  8. Python数据可视化的四种简易方法

    摘要: 本文讲述了热图.二维密度图.蜘蛛图.树形图这四种Python数据可视化方法. 数据可视化是任何数据科学或机器学习项目的一个重要组成部分.人们常常会从探索数据分析(EDA)开始,来深入了解数据, ...

  9. python文件逐行读取四种方法

    下面是四种Python逐行读取文件内容的方法, 并分析了各种方法的优缺点及应用场景,以下代码在python3中测试通过, python2中运行部分代码已注释,稍加修改即可. 方法一:readline函 ...

随机推荐

  1. nginx、Apache、Lighttpd启用HSTS

    302跳转 通常情况下,我们将用户的 HTTP 请求 302 跳转到 HTTPS,这会存在两个问题: 不够安全,302 跳转会暴露用户访问站点,也容易被劫持 拖慢访问速度,302 跳转需要一个 RTT ...

  2. Fluent UDF【5】:第一个UDF

    这里以一个简单的初始化案例来描述UDF的使用过程. 0 Fluent中的Patch Fluent中提供了全域初始化以及局部Patch功能.对于整体区域的全局初始化可以采用starndard及hybri ...

  3. (原创)结构体自动化转为char数组的实现

    结构体自动化转换为char数组这个需求,来自于一个最近开发的一个项目,在项目开发过程中遇到一个小问题,需要将各种结构体拷贝到char数组中,这对于一个简单的结构体来说是很简单的事情,比如下面这个只有整 ...

  4. linux Ctrl+z和Ctrl+c的区别

    1.Ctrl+z 挂起进程,并不会结束,执行fg命令可以重新启动这个被挂起的命令. 2.Ctrl+c 终止进程

  5. 【Session】Tomcat中Session持久化到文件系统或数据库

    参考的优秀文章 Tomcat Session 持久化 Package org.apache.catalina.session 最近同事在做Session外置的功能,我对Session持久化.共享也不太 ...

  6. 【C/C++】嵌入式程序员应该知道的0X10个C语言问题

    一.预处理器(Preprocessor) 1 . 用预处理指令#define 声明一个常数,用以表明 1 年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 6 ...

  7. 搭建kafka源码开发环境时使用"gradle idea"命令构建源码失败

    我的环境: JDK: 1.8.0_131 Gradle: Gradle 3.1 Kafka源码包: kafka-0.10.0.1-src.tgz Zookeeper安装包: zookeeper-3.4 ...

  8. 备份Android机上的照片

    [本文出自天外归云的博客园] 一年一度的春节放假开始了,今天收拾柜子发现了一台上大学时候用的android机,里面有几百张当年的回忆. 写了个shell脚本遍历了下照片存放的路径,然后用一个pytho ...

  9. mybatis中mysql和oracle的差异

    1.applicationContext.xml中的配置差异: 在applicationContext.xml的数据源dataSource的配置中,mysql数据库需要心跳包的配置,而oracle中不 ...

  10. Eigen教程(1)

    整理下Eigen库的教程,参考:http://eigen.tuxfamily.org/dox/index.html 简介 Eigen是C++中可以用来调用并进行矩阵计算的一个库,简单了说它就是一个c+ ...