Python邮件处理:POP与SMTP

发布于:2025-05-24 ⋅ 阅读:(18) ⋅ 点赞:(0)

poplib简介

poplib 是Python 3中的官方邮件库,实现了POP的标准:RFC1939,用于邮件的收取。与之类似的还有imaplib

(注:本文仅拿pop举例)

poplib的使用方法,就是几步:

  1. 先创建一个poplib.POP3类的实例(如果使用SSL,则是poplib.POP3_SSL类的实例)
  2. 之后使用userpass_设置认证
  3. 再使用list获取邮件列表
  4. 根据列表序号,使用retr收取邮件。
  5. (可选)使用delete删除邮件。

另外,如果需要调试,可以使用类的set_debug方法,打印详细的报文交互过程。

如:

def get_mail(host, port, is_ssl, is_debug, username, password):
    if is_ssl:
        server = poplib.POP3_SSL(host, port)

    else:
        server = poplib.POP3(host, port)

    # 开启debug模式,将会打印详细的报文交互过程,便于调试
    server.set_debuglevel(is_debug)
    if is_debug:
        print(server.getwelcome().decode('utf-8'))

    server.user(username)
    server.pass_(password)
    if is_debug:
        print('Messages: %s. Size: %s' % server.stat())

    resp, mails, octets = server.list()
    if is_debug:
        print(mails)

    for index in range(1, len(mails) + 1, 1):
        # 下文将自定义自定义函数,收取邮件
        if get_mail(server, index):
            server.dele(index)

解析邮件为EmailMessage

解析邮件的时候,可以使用Python 3的email 库。

我们使用email的parser.Parser类实例,把邮件内容解析成一个message.EmailMessage实例。

如:

def get_mail(server, index):
    resp, lines, octets = server.retr(index)
    if resp != b'+OK':
        raise Exception


    content = b'\r\n'.join(lines).decode('utf-8')
    msg = Parser().parsestr(content)
    return msg

获取邮件中的附件

如果我们需要获取邮件中的附件,可以进一步解析EmailMessage。

解析EmailMessage的时候,需要注意有的邮件文本也是以附件形式放入邮件中的。所以,需要判断附件的名字是不是message。

正文的文本有可能使用了非ASCII的编码,可以通过EmailMessage的get_charset方法取得。如果取得失败,再通过ContentType域取得。

def guess_charset(msg):  
    charset = msg.get_charset()    
    if charset:  
        return charset  
  
    content_type = msg.get('Content-Type', '').lower()  
    pos = content_type.find('charset=')
    if pos >= 0:
        remain = content_type[pos + 8:].strip()  
        pos = remain.find(';')  
        if pos >= 0:  
            return remain[:pos]  
  
        return remain  
  
    return None

最后完整的解析代码如下:

from email.utils import parseraddr
from email.header import decode_header

def save_attachment(path, msg):
    content_type = msg.get_content_type()
    content = msg.get_payload(decode=True)
    if content_type == 'text/plain' or content_type == 'text/html':
        charset = guess_charset(msg)
        if charset:
            content = content.decode(charset)

        else:
            content = content.decode('utf-8')

    with open(path) as fp:
        fp.write(content)

def parse_message(msg)
    # 获取邮件主题
    subject = msg.get('Subject', '')
    # 获取发件人
    from_value, from_addr = parseaddr(msg.get('From', ''))
    # 获取收件人
    to_value, to_addr = parseaddr(msg.get('To', ''))
    
    # 判断是否有附件
    if msg.is_multipart():
        parts = msg.get_payload()
        for n, part in enumerate(parts):
            name = part.get_filename('message')
            if name == 'message':
                save_attachment('message', part)

            else:
                realname = decode_str(name)
                attapath = os.path.join(attachments_path, realname)
                save_attachment(attapath, part)

    else:
        save_attachment('message', msg)

使用smtplib发送邮件

发送邮件相对来说比收取简单,因为格式我们可以控制,只要采用最简单标准的就行了。

先生成一个EmailMessage。

def generate_message(subject, from, to, message, attchment_list):
    if len(attchment_list) == 0:
        message = MIMEText(message, 'plain', 'gb2312')

    else:
        message = MIMEMultipart()
        message.attach(MIMEText(message, 'plain', 'gb2312'))

    message['Subject'] = Header(subject, 'gb2312')
    message['From'] = from
    message['To'] = to

    # attachment_list,是一个列表,每一项是一个包含Content-Type与路径的字典。
    for att in attachment_list:
        add_attachment(message, att['content-type'], att['path'])

    return message

追加附件的实现如下:

def add_attachment(message, contenttype, path):
    # Content-Type一般是text/html这种格式
    appname, subtype = contenttype.split('/')
    
    # 取得文件名作为附件里文件的名字,加入附件EmailMessage的Header
    _dirname, basename = os.path.split(path)
    encodedname = Header(basename, 'gb2312').encode()
    att = MIMEBase(appname, subtype, name=encodedname)
    att.add_header('Content-Disposition', 'attachment', filename=encodedname)

    # 加入附件内容
    att.set_payload(open(path, 'rb').read(), 'base64')

    message.attach(att)

使用smtplib发送的过程,类似与poplib。

  1. 创建一个SMTP(或者SMTP_SSL)实例
  2. 连接服务端(connect)
  3. 登录服务端(login)
  4. 发送邮件(sendmail)
import smtplib

def smtp_send(host, port, is_ssl, username, password, sender, receivers, message):
    if is_ssl:
        server = smtplib.SMTP_SSL()

    else:
        server = smtplib.SMTP()

    server.connect(host=host, port=port)
    server.login(username, password)
    server.sendmail(sender, receivers, message.as_string())

网站公告

今日签到

点亮在社区的每一天
去签到