Python 操作电子邮件方法

smtp和Pop3发送和接收电子邮件方法简介

Posted by xiaoh on February 16, 2016

这篇文章大概就是一个源码分享,介绍了用Python来发送电子邮件的方式 文章参考:Python电子邮件


SMTP发送邮件

SMTP是发送邮件的协议,Python内置对SMTP的支持,可以发送纯文本邮件、HTML邮件以及带附件的邮件。

Python对SMTP支持有smtplib和email两个模块,email负责构造邮件,smtplib负责发送邮件。

以下是各种电子邮件模式的发送代码:

    #!/usr/bin/python
    #-*- coding:utf-8 -*-
    
    #############################################
    # File Name: send_email.py
    # Author: xiaoh
    # Mail: xiaoh@about.me
    # Created Time:  2016-02-16 14:55:55
    #############################################
    
    import smtplib
    import ConfigParser
    from email import encoders
    from email.header import Header
    from email.mime.base import MIMEBase
    from email.mime.text import MIMEText
    from email.mime.multipart import MIMEMultipart
    from email.utils import parseaddr, formataddr
    
    cfg = ConfigParser.ConfigParser()
    
    cfg.read('email.conf')
    from_addr = cfg.get('email', 'from')
    password = cfg.get('email', 'pass')
    smtp_serv = cfg.get('email', 'smtp')
    to_addr = cfg.get('email', 'sdto')
    
    def _format_addr(s):
        name, addr = parseaddr(s)
        return formataddr(( \
            Header(name, 'utf-8').encode(), \
            addr.encode('utf-8') if isinstance(addr, unicode) else addr))
    
    def send_text():
        msg = MIMEText('hello, send by script coding by xiaoh.', 'plain', 'utf-8')
        msg['From'] = _format_addr(u'xiaoh <%s>' % from_addr)
        msg['To'] = _format_addr(u'管理员 <%s>' % to_addr)
        msg['Subject'] = Header(u'来自SMTP的问候……', 'utf-8').encode()
        send(msg)
    
    def send_html():
        msg = MIMEText('<html><body><h1>Hello</h1>' +
            '<p>welcome to my <a href="http://www.xiaoh.me">Blog</a>...</p>' +
            '</body></html>', 'html', 'utf-8')
        msg['From'] = _format_addr(u'xiaoh <%s>' % from_addr)
        msg['To'] = _format_addr(u'管理员 <%s>' % to_addr)
        msg['Subject'] = Header(u'来自SMTP的问候……', 'utf-8').encode()
        send(msg)
    
    def send_attachment():
        msg = MIMEMultipart()
        msg['From'] = _format_addr(u'xiaoh <%s>' % from_addr)
        msg['To'] = _format_addr(u'管理员 <%s>' % to_addr)
        msg['Subject'] = Header(u'来自SMTP的问候……', 'utf-8').encode()
        # 邮件正文是MIMEText:
        msg.attach(MIMEText('send with file...', 'plain', 'utf-8'))
        # 添加到MIMEMultipart:
        msg.attach(read_image())
        send(msg)
    
    def send_image():
        msg = MIMEMultipart()
        msg['From'] = _format_addr(u'xiaoh <%s>' % from_addr)
        msg['To'] = _format_addr(u'管理员 <%s>' % to_addr)
        msg['Subject'] = Header(u'来自SMTP的问候……', 'utf-8').encode()
        # 邮件正文是MIMEText:
        msg.attach(MIMEText('<html><body><h1>Hello</h1>' +
                        '<p><a href=http://www.xiaoh.me><img src="cid:0"></a></p>' +
                        '</body></html>', 'html', 'utf-8'))
        # 添加到MIMEMultipart:
        msg.attach(read_image())
        send(msg)
    
    def send_multi():
        msg = MIMEMultipart('alternative')
        msg['From'] = _format_addr(u'xiaoh <%s>' % from_addr)
        msg['To'] = _format_addr(u'管理员 <%s>' % to_addr)
        msg['Subject'] = Header(u'来自SMTP的问候……', 'utf-8').encode()
        # 邮件正文是MIMEText:
        msg.attach(MIMEText('hello, this is my script.', 'plain', 'utf-8'))
        msg.attach(MIMEText('<html><body><h1>Hello</h1>' +
                        '<p><a href=http://www.xiaoh.me><img src="cid:0"></a></p>' +
                        '</body></html>', 'html', 'utf-8'))
        # 添加到MIMEMultipart:
        msg.attach(read_image())
        msg.attach(read_image())
        send(msg)
    
    def read_image():
        # 添加附件就是加上一个MIMEBase,从本地读取一个图片:
        with open('/home/xingming/gitpro/blogs/img/post-bg-default.jpg', 'rb') as f:
            # 设置附件的MIME和文件名,这里是png类型:
            mime = MIMEBase('image', 'png', filename='test.png')
            # 加上必要的头信息:
            mime.add_header('Content-Disposition', 'attachment', filename='test.png')
            mime.add_header('Content-ID', '<0>')
            mime.add_header('X-Attachment-Id', '0')
            # 把附件的内容读进来:
            mime.set_payload(f.read())
            # 用Base64编码:
            encoders.encode_base64(mime)
            return mime
    
    def send(msg):
        server = smtplib.SMTP(smtp_serv, 25) # 25为SMTP协议的默认端口
        server.ehlo()
        server.starttls()
        server.set_debuglevel(1)
        server.login(from_addr, password)
        server.sendmail(from_addr, [to_addr], msg.as_string())
        server.quit()
    
    def main():
    #    send_text()
    #    send_html()
    #    send_attachment()
    #    send_image()
        send_multi()
        print 'hello'
    
    if __name__ == "__main__":
        main()

POP接收邮件

Python内置一个poplib模块,实现了POP3协议,可以直接用来收邮件。

注意到POP3协议收取的不是一个已经可以阅读的邮件本身,而是邮件的原始文本,这和SMTP协议很像,SMTP发送的也是经过编码后的一大段文本。

要把POP3收取的文本变成可以阅读的邮件,还需要用email模块提供的各种类来解析原始文本,变成可阅读的邮件对象。

所以,收取邮件分两步:

第一步:用poplib把邮件的原始文本下载到本地;

第二部:用email解析原始文本,还原为邮件对象。

    #!/usr/bin/python
    #-*- coding:utf-8 -*-
    
    #############################################
    # File Name: recv_email.py
    # Author: xiaoh
    # Mail: xiaoh@about.me
    # Created Time:  2016-02-16 17:33:29
    #############################################
    
    import ConfigParser
    import poplib
    import email
    from email.parser import Parser
    from email.header import decode_header
    from email.utils import parseaddr
    
    cfg = ConfigParser.ConfigParser()
    
    cfg.read('email.conf')
    email = cfg.get('pop', 'email')
    password = cfg.get('pop', 'pass')
    pop_serv = cfg.get('pop', 'pop')
    
    def recv():
        # 连接到POP3服务器:
        server = poplib.POP3(pop_serv)
        # 可以打开或关闭调试信息:
        # server.set_debuglevel(1)
        # 可选:打印POP3服务器的欢迎文字:
        print(server.getwelcome())
        # 身份认证:
        server.user(email)
        server.pass_(password)
        # stat()返回邮件数量和占用空间:
        print('Messages: %s. Size: %s' % server.stat())
        # list()返回所有邮件的编号:
        resp, mails, octets = server.list()
        # 可以查看返回的列表类似['1 82923', '2 2184', ...]
        # print(mails)
        # 获取最新一封邮件, 注意索引号从1开始:
        index = len(mails)
        print index
        resp, lines, octets = server.retr(index)
        # lines存储了邮件的原始文本的每一行,
        # 可以获得整个邮件的原始文本:
        msg_content = '\r\n'.join(lines)
        # 稍后解析出邮件:
        msg = Parser().parsestr(msg_content)
        print_info(msg)
        # 可以根据邮件索引号直接从服务器删除邮件:
        # server.dele(index)
        # 关闭连接:
        server.quit()
    
    # indent用于缩进显示:
    def print_info(msg, indent=0):
        if indent == 0:
            # 邮件的From, To, Subject存在于根对象上:
            for header in ['From', 'To', 'Subject']:
                value = msg.get(header, '')
                if value:
                    if header=='Subject':
                        # 需要解码Subject字符串:
                        value = decode_str(value)
                    else:
                        # 需要解码Email地址:
                        hdr, addr = parseaddr(value)
                        name = decode_str(hdr)
                        value = u'%s <%s>' % (name, addr)
                print('%s%s: %s' % ('  ' * indent, header, value))
        if (msg.is_multipart()):
            # 如果邮件对象是一个MIMEMultipart,
            # get_payload()返回list,包含所有的子对象:
            parts = msg.get_payload()
            for n, part in enumerate(parts):
                print('%spart %s' % ('  ' * indent, n))
                print('%s--------------------' % ('  ' * indent))
                # 递归打印每一个子对象:
                print_info(part, indent + 1)
        else:
            # 邮件对象不是一个MIMEMultipart,
            # 就根据content_type判断:
            content_type = msg.get_content_type()
            if content_type=='text/plain' or content_type=='text/html':
                # 纯文本或HTML内容:
                content = msg.get_payload(decode=True)
                # 要检测文本编码:
                charset = guess_charset(msg)
                if charset:
                    content = content.decode(charset)
                print('%sText: %s' % ('  ' * indent, content + '...'))
            else:
                # 不是文本,作为附件处理:
                print('%sAttachment: %s' % ('  ' * indent, content_type))
    
    def decode_str(s):
        value, charset = decode_header(s)[0]
        if charset:
            value = value.decode(charset)
        return value
    
    def guess_charset(msg):
        # 先从msg对象获取编码:
        charset = msg.get_charset()
        if charset is None:
            # 如果获取不到,再从Content-Type字段获取:
            content_type = msg.get('Content-Type', '').lower()
            pos = content_type.find('charset=')
            if pos >= 0:
                charset = content_type[pos + 8:].strip()
        return charset
    
    def main():
        recv()
        print 'hello'
    
    if __name__ == "__main__":
        main()

END