• 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服务器连接

    看下官方给的示例:

    复制代码
    >>> 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)
    {}
    复制代码

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

    wKioL1gzo3ry0nxHAABIwiKIKIM250.png

    可以看到,多个收件人可以放到一个列表中进行群发。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)
    {}
    复制代码
     

    wKiom1gzo_2j6KkgAABXJfMQ1Kg062.png

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

    wKioL1gzpD_DOmxOAABXFj0zUKE200.png

    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, "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运维开发群)

    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, "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

    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, "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

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

    原文地址: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()
  • 相关阅读:
    jquery获得option的值和对option进行操作
    laravel 在添加操作自动完成对时间保存修改
    laravel使用ajax
    mysql操作查询结果case when then else end用法举例
    Laravel框架数据库CURD操作、连贯操作总结
    laravel5.1关于lists函数的bug
    详解AngularJS中的filter过滤器用法
    javascript中的时间处理
    angularJs--$on、$emit和$broadcast的使用
    angularJs--<ui-select>
  • 原文地址:https://www.cnblogs.com/klb561/p/10170305.html
Copyright © 2020-2023  润新知