发邮件现成库: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()