解析邮件body的几种方法

发邮件现成库:zmail、yagmail

imapclient  pyzmail  pyzmail36 v1.04   PyEmail v0.0.1

IMAPClient  

Author:Menno Finlay-Smits
Version:2.2.0
Date:Jan 16, 2021
Homepage:http://imapclient.freshfoo.com
Download:http://pypi.python.org/pypi/IMAPClient/
Source code:https://github.com/mjs/imapclient
Documentation:http://imapclient.readthedocs.io/
License:New BSD License
Support:Mailing List

Introduction

IMAPClient is an easy-to-use, Pythonic and complete IMAP client library.

参考:

https://blog.csdn.net/epubit17/article/details/105930758/  用Python发送邮件,需要这样三步

https://www.freesion.com/article/20401226572/  PYTHON自动接收邮件

https://blog.csdn.net/u012972003/article/details/80529147 基于Python的邮件收发

https://www.cnblogs.com/lsdb/p/9419036.html  Python3+smtplib+poplib+imaplib实现发送和收取邮件(以qq邮箱为例)

获取电子邮件是个轻松简单的步骤,但获取到的是邮件原始内容,其内容格式通常遵守MIME协议(多用途互联网邮件扩展),需要一番解析才能正常显示。

一封邮件通常会分为邮件头与邮件体,邮件头中存在着这份邮件的基本信息,而邮件体则是邮件的具体内容

multipart/mixed
multipart/alternative
text/html
text/plain

电子邮件可以是纯文本、HTML或者两者混合

纯文本电子邮件只包含文本,而HTML电子邮件可以有颜色、字体、图像、和其他功能

如果电子邮件是纯文本,PyzMessage对象会将html_part属性设为None,同样,当邮件只有HTML,PyzMessage对象会将text_part属性设置为None

否则,text_part或者html_part将有一个get_payload()方法,将电子邮件的正文返回为bytes数据类型,但是这仍然不是我们可以使用的字符串,
最后一步对get_payload()返回的bytes值调用decode()方法。decode()方法接受一个参数:这条消息的字符编码,保存在text_part.charset或
html_part.charset属性中
 

收取邮件的过程:

1、登陆并获得邮件列表

2、遍历列表,每个item转换为email.message对象 【email.message_from_string() 】

3、分别解释email.message对象的Header部分和Body部分

3.1 其中body部分要根据其信息类型(text 还是 multipart)采取不同的解析方案。

3.1.1 对于 纯文本和html文本,直接用 msg.get_payload(decode=True).decode(charset)

3.1.2 对于multipart类型,遍历其内部的每个部分(段体)进行解析。由于段体可以内嵌子段,所以需要用递归调用

3.1.2.1 如果其中一个段体或子段体是文本类型,那就直接  msg.get_payload(decode=True).decode(charset)

【body的结构跟段的结构是一样的,可以看中body就是最外层的段。解析段的代码可以直接用于解析body。段也可以理解成body中的一个子body】

对于遍历body,官方提供了walk()方法。比递归更简单, 使用链接:

email.message: Representing an email message — Python 3.9.6 documentation

walk()方法可以遍历body的所有parts和subparts

1、递归遍历,并根据charset进行decode

# -*- coding: utf-8 -*-
 
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr
 
import poplib
 
# 输入邮件地址, 口令和POP3服务器地址:
email = input('Email: ')
password = input('Password: ')
pop3_server = input('POP3 server: ')
 
 
def guess_charset(msg):  # 获取邮件编码
    charset = msg.get_charset()
    if charset is None:
        content_type = msg.get('Content-Type', '').lower()
        pos = content_type.find('charset=')
        if pos >= 0:
            charset = content_type[pos + 8:].strip()
    return charset
 
 
def decode_str(s):
    value, charset = decode_header(s)[0]
    if charset:
        value = value.decode(charset)
    return value
 
 
def print_info(msg, indent=0):
    if indent == 0:
        # for循环用来打印邮件头信息
        for header in ['From', 'To', 'Subject']:  # 邮件头信息包括主题、收件人、发件人
            value = msg.get(header, '')  # 获取 某个头信息的值
            if value:   # 如果该头信息值不为空
                if header == 'Subject':  # 如果是 主题
                    value = decode_str(value)   # 将主题内容解码,对应着发邮件时的Head().encode()
                else:   # 如果是收件人或发件人
                    hdr, addr = parseaddr(value)  # 先将收件人和发件人的名称与地址拆开,对应formataddr()函数
                    name = decode_str(hdr)  # 再将收件人发件人名称部分解码,对应着发邮件时的Head().encode()
                    value = u'%s <%s>' % (name, addr)  # 再进行组装
            print('%s%s: %s' % ('  ' * indent, header, value))
    # 如果该邮件同时支持HTML和Plain格式,则说明该邮件可能由多钟结构组成
    if (msg.is_multipart()):
        parts = msg.get_payload()  # get_payload这这里会返回一个list,代表multipart包含的邮件对象列表
        for n, part in enumerate(parts):  # 通过enumerate创建枚举对象,n代表索引
            print('%spart %s' % ('  ' * indent, n))
            print('%s--------------------' % ('  ' * indent))
            print_info(part, indent + 1)  # 递归调用
    else:
        content_type = msg.get_content_type()  # 获取发送邮件支持的类型、文本或html
        if content_type == 'text/plain' or content_type=='text/html':
            content = msg.get_payload(decode=True)  # 提取文本内容并解码,这里的解码是对邮件信息解码,不是平时常用的byte解码
            charset = guess_charset(msg)  # 获得邮件编码
            if charset:
                content = content.decode(charset)  # 根据获得到的编码对byte进行解码
            print('%sText: %s' % ('  ' * indent, content + '...'))
            print('%sAttachment: %s' % ('  ' * indent, content_type))
 
 
# 连接到POP3服务器:
# server = poplib.POP3(pop3_server)
server = poplib.POP3_SSL(pop3_server, 995)
# 可以打开或关闭调试信息:
server.set_debuglevel(1)
# 可选:打印POP3服务器的欢迎文字:
print(server.getwelcome().decode('utf-8'))
# 身份认证:
server.user(email)
server.pass_(password)
# stat()返回邮件数量和占用空间:
print('Messages: %s. Size: %s' % server.stat())
# list()返回所有邮件的编号:
resp, mails, octets = server.list()
# 可以查看返回的列表类似[b'1 82923', b'2 2184', ...]
print(mails)
# 获取最新一封邮件, 注意索引号从1开始:
index = len(mails)
resp, lines, octets = server.retr(index)
# lines存储了邮件的原始文本的每一行,
# 可以获得整个邮件的原始文本:
msg_content = b'\r\n'.join(lines).decode('utf-8')
# 解析邮件,对应encode_base64
msg = Parser().parsestr(msg_content)
print_info(msg)
# 可以根据邮件索引号直接从服务器删除邮件:
# server.dele(index)
# 关闭连接:
server.quit()
————————————————
原文链接:https://blog.csdn.net/weixin_40247263/article/details/82670895

2、walk遍历

# Python 3.6
# Author:宿命小哥哥
import imaplib
import email
 
 
# 解析邮件方法(区分出正文与附件)
def parseEmail(e):
    # 解析邮件/信体
    # 循环信件中的每一个mime的数据块
    for part in e.walk():
 
        # 这里要判断是否是multipart,是的话,里面的数据是一个message 列表
        if not part.is_multipart():
            charset = part.get_charset()
            print('charset: ', charset)
            contenttype = part.get_content_type()
            print('content-type', contenttype)
            # 如果是附件,这里就会取出附件的文件名
            if contenttype in ['image/jpeg', 'image/png']:
                image_file = part.get_payload(decode=True)
                image_file_name = part.get_filename()
                print('content-type', image_file_name)
            name = part.get_param("name")
            if name:
                # 有附件
                # 下面的三行代码只是为了解码象=?gbk?Q?=CF=E0=C6=AC.rar?=这样的文件名
                fh = email.Header.Header(name)
                fdh = email.Header.decode_header(fh)
                fname = fdh[0][0]
                print('附件名:', fname)
                # 解码出附件数据,然后存储到文件中
                attach_data = part.get_payload(decode=True)
 
                try:
                    # 注意一定要用wb来打开文件,因为附件一般都是二进制文件
                    f = open(fname, 'wb')
                except:
                    print('附件名有非法字符,自动换一个')
                    f = open('aaaa', 'wb')
                f.write(attach_data)
                f.close()
            else:
                # 不是附件,是文本内容,解码出文本内容,直接输出
                print(part.get_payload(decode=True).decode('utf-8'))
 
 
def geimail(host, port, mail, password):
    M = imaplib.IMAP4_SSL(host, port)
    print(M)
    try:
        try:
            M.login(mail, password)
            print('成功登陆邮箱')
        except Exception as e:
            print('登陆错误: %s' % e)
            M.close()
        # 选取收件箱
        M.select('INBOX')
        # 获取未读邮件信息
        unseen = M.search(None, 'Unseen')
        unseen_list = unseen[1][0].split()
        print('未读邮件数:', len(unseen_list))
        # 想获取最新一封的做法是:
        typ, data = M.search(None, 'ALL')
        msglist = data[0].split()
        print('已读邮件数:', len(msglist))
        new = msglist[len(msglist) - 1]
        try:
            typ, data = M.fetch(new, '(RFC822)')
            msg = email.message_from_string(data[0][1].decode('utf-8'))
            sub = email.header.decode_header(msg.get('subject'))[0]
            print("From:", email.utils.parseaddr(msg.get("from"))[1])
            print("To:", email.utils.parseaddr(msg.get("to"))[1])
            if not email.utils.parseaddr(msg .get_all('cc'))[1]:
                print('Cc: 无抄送者')
            else:
                print('Cc:', email.utils.parseaddr(msg .get_all('cc'))[1])
            print("Subject:", sub[0].decode(sub[1]))
            print("Date:", msg["Date"])
            print("_______________________________")
            return msg
        except Exception as e:
            print('got msg error: %s' % e)
        M.close()
    except Exception as e:
        print('imap error: %s' % e)
        M.close()
    M.logout()
 
 
if __name__ == "__main__":
    # 输入邮件地址, 口令和服务器地址:
    Email = input('Email: ')
    Password = input('Password: ')
    IMAP_server = input('IMAP server: ')
    IMAP_port = input('IMAP port: ')
    print('【1】开始获取最新一封邮件...')
    Msg = geimail(IMAP_server, IMAP_port, Email, Password)
    print('【2】开始解析邮件内容...')
    parseEmail(Msg)
————————————————
 
原文链接:https://blog.csdn.net/u012972003/article/details/80529147
import poplib
#解析邮件
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr

#解析消息头中的字符串
#没有这个函数,print出来的会使乱码的头部信息。如'=?gb18030?B?yrXWpL3hufsueGxz?='这种
#通过decode,将其变为中文
def decode_str(s):
    value, charset = decode_header(s)[0]
    if charset:
        value = value.decode(charset)
    return value

#解码邮件信息分为两个步骤,第一个是取出头部信息
#首先取头部信息
#主要取出['From','To','Subject']
'''
From: "=?gb18030?B?anVzdHpjYw==?=" <justonezcc@sina.com>
To: "=?gb18030?B?ztLX1Ly6tcTTys/k?=" <392361639@qq.com>
Subject: =?gb18030?B?dGV4dMTjusM=?=
'''
#如上述样式,均需要解码
def get_header(msg):
    for header in ['From', 'To', 'Subject']:
        value = msg.get(header, '')
        if value:
            #文章的标题有专门的处理方法
            if header == 'Subject':
                value = decode_str(value)
            elif header in ['From','To']:
            #地址也有专门的处理方法
                hdr, addr = parseaddr(value)
                name = decode_str(addr)
                #value = name + ' < ' + addr + ' > '
                value=name
        print(header + ':' + value)
#头部信息已取出


#获取邮件的字符编码,首先在message中寻找编码,如果没有,就在header的Content-Type中寻找
def guess_charset(msg):
    charset = msg.get_charset()
    if charset is None:
        content_type = msg.get('Content-Type', '').lower()
        pos = content_type.find('charset=')
        if pos >= 0:
            charset = content_type[pos+8:].strip()
    return charset


#邮件正文部分
#取附件
#邮件的正文部分在生成器中,msg.walk()
#如果存在附件,则可以通过.get_filename()的方式获取文件名称

def get_file(msg):
    for part in msg.walk():
        filename=part.get_filename()
        if filename!=None:#如果存在附件
            filename = decode_str(filename)#获取的文件是乱码名称,通过一开始定义的函数解码
            data = part.get_payload(decode = True)#取出文件正文内容
            #此处可以自己定义文件保存位置
            path=filename
            f = open(path, 'wb')
            f.write(data)
            f.close()
            print(filename,'download')

def get_content(msg):
    for part in msg.walk():
        content_type = part.get_content_type()
        charset = guess_charset(part)
        #如果有附件,则直接跳过
        if part.get_filename()!=None:
            continue
        email_content_type = ''
        content = ''
        if content_type == 'text/plain':
            email_content_type = 'text'
        elif content_type == 'text/html':
            print('html 格式 跳过')
            continue #不要html格式的邮件
            email_content_type = 'html'
        if charset:
            try:
                content = part.get_payload(decode=True).decode(charset)
            except AttributeError:
                print('type error')
            except LookupError:
                print("unknown encoding: utf-8")
        if email_content_type =='':
            continue
            #如果内容为空,也跳过
        print(email_content_type + ' -----  ' + content)

#get_file(msg)
if __name__ == '__main__':
    
    email='392361639@qq.com'
    password='ngq*******rznbici'

    server=poplib.POP3_SSL('pop.qq.com')
    server.user(email)
    server.pass_(password)
    #登录的过程
    resp, mails, octets = server.list()
    index = len(mails)#邮件的总数
    #此处的循环是取最近的几封邮件
    for i in range(index-2,index+1):
        resp, lines, octets = server.retr(i)#取邮件
        msg_content = b'\r\n'.join(lines).decode('utf-8','ignore')
        msg = Parser().parsestr(msg_content)
        #server.dele(index) 删除邮件
        get_header(msg)
        get_file(msg)
        get_content(msg)
    server.quit()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值