在写脚本时,放到后台运行,想知道执行情况,会通过邮件、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服务器连接 |
看下官方给的示例:
1
2
3
4
5
6
7
8
9
10
11
|
>>>
import
smtplib
>>> s
=
smtplib.SMTP(
"localhost"
)
>>> tolist
=
[
"[email protected]"
,
"[email protected]"
,
"[email protected]"
,
"[email protected]"
]
>>> msg
=
'''\
... From: [email protected]
... Subject: testin'...
...
... This is a test '''
>>> s.quit()
|
我们根据示例给自己发一个邮件测试下:
我这里测试使用本地的SMTP服务器,也就是要装一个支持SMTP协议的服务,比如sendmail、postfix等。
CentOS安装sendmail:yum install sendmail
1
2
3
4
5
6
7
8
9
|
>>>
import
smtplib
>>> s
=
smtplib.SMTP(
"localhost"
)
>>> msg
=
'''\
... From: [email protected]
... Subject: test
... This is a test '''
{}
|
进入腾讯和网易收件人邮箱,就能看到刚发的测试邮件,一般都被邮箱服务器过滤成垃圾邮件,所以收件箱没有,你要去垃圾箱看看。
可以看到,多个收件人可以放到一个列表中进行群发。msg对象里From表示发件人,Subject是邮件标题,换行后输入的是邮件内容。
上面是使用本地SMTP服务器发送的邮件,测试下用163服务器发送邮件看看效果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
>>>
import
smtplib
>>> s
=
smtplib.SMTP(
"smtp.163.com"
)
(
235
,
'Authentication successful'
)
>>> msg
=
'''\
... From: [email protected]
... Subject: test
... This is a test '''
Traceback (most recent call last):
File
"
, line
1
,
in
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对象,添加上收件人:
1
2
3
4
5
6
7
8
|
>>> msg
=
'''\
... From: [email protected]
... To: [email protected] ,[email protected]
... Subject: test
...
... This is a test '''
{}
|
好了,可以正常发送邮件了。msg这个格式是SMTP规定的,一定要遵守。
14.1 Python发送邮件并抄送
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
#!/usr/bin/python
# -*- coding: utf-8 -*-
import
smtplib
def
sendMail(body):
smtp_server
=
'smtp.163.com'
mail_pass
=
'xxx'
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,
'25'
)
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的信息去掉即可。
14.2 Python发送邮件带附件
由于SMTP.sendmail()方法不支持添加附件,所以可以使用email模块来满足需求。email模块是一个构造邮件和解析邮件的模块。
先看下如何用email库构造一个简单的邮件:
1
2
3
4
5
6
|
message
=
Message()
message[
'Subject'
]
=
'邮件主题'
message[
'From'
]
=
from_mail
message[
'To'
]
=
to_mail
message[
'Cc'
]
=
cc_mail
message.set_payload(
'邮件内容'
)
|
基本的格式就是这样的!
继续回到主题,发送邮件带附件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
#!/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'
mail_pass
=
'xxx'
# 构造一个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,
"25"
)
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运维开发群)
14.3 Python发送HTML邮件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
#!/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'
mail_pass
=
'xxx'
# 构造一个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,
"25"
)
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
=
"""
"""
sendMail(body)
|
14.4 Python发送图片邮件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#!/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'
mail_pass
=
'xxx'
# 构造一个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'
))
|