第十四章 Python发送邮件(常见四种邮件内容)

简介:

在写脚本时,放到后台运行,想知道执行情况,会通过邮件、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 = [ "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

1
2
3
4
5
6
7
8
9
>>>  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)
{}

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

wKioL1gzo3ry0nxHAABIwiKIKIM250.png

可以看到,多个收件人可以放到一个列表中进行群发。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" )
>>> 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对象,添加上收件人:

1
2
3
4
5
6
7
8
>>> 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)
{}

wKiom1gzo_2j6KkgAABXJfMQ1Kg062.png

好了,可以正常发送邮件了。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'
     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 :
         =  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!" )

wKioL1gzpD_DOmxOAABXFj0zUKE200.png

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'
     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 :
         =  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' )

wKioL1gzpLrwrjscAAB4kh5xWwY921.png-wh_50

博客地址: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'
     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 :
         =  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  =  """
     <h1>测试邮件</h1>
     <h2 style="color:red">This is a test</h1>
     """
     sendMail(body)

wKioL1gzpUHyqoafAABa04eLS1A073.png

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'
     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 :
         =  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  =  """
     <h1>测试图片</h1>
     <img src="cid:image1"/>    # 引用图片
     """
     sendMail(body,  'test.png' )

wKiom1gzpXOjnJQEAAKvLRjLBhw508.png

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



本文转自 李振良OK 51CTO博客,原文链接:http://blog.51cto.com/lizhenliang/1875330,如需转载请自行联系原作者

相关文章
|
5天前
|
数据安全/隐私保护 Python
Python实现邮件发送(含详细设置方法),并总结自己遇到的错误
Python实现邮件发送(含详细设置方法),并总结自己遇到的错误
|
5天前
|
存储 搜索推荐 数据安全/隐私保护
python实战讲解之使用Python批量发送个性化邮件
python实战讲解之使用Python批量发送个性化邮件
|
6月前
|
Python
python实现发送邮件demo
python实现发送邮件demo
39 1
|
6月前
|
Unix 数据安全/隐私保护 Python
python自动生成Excel表格数据并发送邮件案例
python自动生成Excel表格数据并发送邮件案例
|
7月前
|
数据安全/隐私保护 Python
python 发送邮件demo
python 发送邮件demo
|
5天前
|
Python
Python自动化办公实战案例:文件整理与邮件发送
Python自动化办公实战案例:文件整理与邮件发送
10 0
|
5天前
|
运维 Shell Linux
第十四章 Python发送邮件(常见四种邮件内容)
第十四章 Python发送邮件(常见四种邮件内容)
|
5天前
|
监控 Python
Python监控主机是否存活,并发报警邮件
Python监控主机是否存活,并发报警邮件
|
5天前
|
数据安全/隐私保护 Python
Python Flask-Mail实现邮件发送
Python Flask-Mail实现邮件发送
|
5天前
|
存储 Python
终于,手把手教会 HR 实现 Python + Excel 「邮件自动化」发工资条了
终于,手把手教会 HR 实现 Python + Excel 「邮件自动化」发工资条了
32 0