在前面的教程中,我们已经详细介绍了如何使用SMTP协议发送电子邮件。本节教程中我们将介绍如何使用Python接收邮件。接收邮件有两种常用的协议分别为POP3协议和IMAP协议。
POP3协议是指Post Office Protocol(version 3)(邮局协议第3版),是一种简单的邮件接收协议,允许电子邮件客户端下载邮件服务器上的邮件,但是在客户端的操作(如移动邮件、标记已读等)不会同步到邮件服务器上,比如通过客户端收取了邮箱的两封邮件并移动到其他文件夹,邮件服务器上的这些邮件不会被同步移动。POP3协议的优点在于其简单易用,适用于个人用户和小型企业。但是,由于它不支持邮件在服务器上的同步更新,因此不适合多设备同步使用的场景。
IMAP协议是指Internet Mail Access Protocol(互联网邮件访问协议),是一种更先进的电子邮件接收协议。与POP3不同,它提供了邮件服务器与电子邮件客户端之间的双向通信,任何在客户端做的改变都会同步到邮件服务器上。例如,删除邮件,标记已读等,服务器上的邮件也会做相应的动作。所以无论从浏览器登录邮箱或者客户端软件登录邮箱,看到的邮件以及状态都是一致的;而POP3在客户端的操作不会反馈到服务器上。
1、使用POP3协议接收邮件
Python内置一个poplib模块,实现了POP3协议,可以直接用来接收邮件。使用poplib模块收取邮件也很简单,该模块同样提供了poplib.POP3和 poplib.POP3_SSL两个类,分别用于连接普通的POP服务器和基于SSL的POP服务器。以下是一个简单的Python脚本,演示了如何通过SMTP服务器发送邮后再通过POP3服务器接收邮件。
动手练一练:
import smtplib
import poplib
from time import sleep
# 发件人邮箱账号
from_addres = "5********8@qq.com"
# 发件人邮箱密码,即配置生成的授权码
password = "m***********b"
# 收件人邮箱账号,你可以填任意想要发送的邮箱,也可以发送给自己
to_addres = "5********8@qq.com"
# 邮件正文
origmsg = '''\
From: %(who)s
To: %(who)s
Subject: Learn Python
Learn Python in www.pyhint.com
''' % {'who': from_addres}
#使用SMTP完成邮件的发送
sendSvr = smtplib.SMTP_SSL("smtp.qq.com",)
# 登录操作
sendSvr.login(from_addres, password)
#参数:发件人,收件人,邮件整体(消息头和消息体的字符串表示)
errs = sendSvr.sendmail(from_addres,to_addres,origmsg)
# 退出服务器
sendSvr.quit()
#,assert返回为假就会触发异常
assert len(errs) == 0, errs
print("QQ邮件发送成功!")
# 睡眠5秒钟,等待邮件被投递,让服务器完成消息的发送和接收。
sleep(5)
# 使用pop3完成邮件的获取,创建一个pop3接收对象
recvSvr = poplib.POP3_SSL("pop.qq.com")
# 设置用户名
recvSvr.user(from_addres)
# 设置密码
recvSvr.pass_(password)
# 获取邮件列表
emailist = recvSvr.stat()
#下载第一个邮件
rsp, msg, siz = recvSvr.retr(emailist[0])
print(msg)
print("pop3接收邮件完成")
上面以QQ邮箱为例,使用smtp发送了一个普通文本邮件,睡眠5秒钟后,等待邮件被投递,再使用pop3协议读取了邮件列表,并下载了第一个邮件,最后成功返回了第一个邮件内容“Learn Python in www.pyhint.com”。
2、使用IMAP协议接收邮件
Python内置一个imaplib模块,实现了IMAP协议接收邮件,与POP3协议接收邮件的操作基本类似。例如:
动手练一练:
import smtplib
import imaplib
import email
from time import sleep
# 发件人邮箱账号
from_addres = "5********8@qq.com"
# 发件人邮箱密码,即配置生成的授权码
password = "m***********b"
# 收件人邮箱账号,你可以填任意想要发送的邮箱,也可以发送给自己
to_addres = "5********8@qq.com"
# 邮件正文
origmsg = '''\
From: %(who)s
To: %(who)s
Subject: Learn Python
Learn Python in www.pyhint.com
''' % {'who': from_addres}
#使用SMTP完成邮件的发送
sendSvr = smtplib.SMTP_SSL("smtp.qq.com",)
# 登录操作
sendSvr.login(from_addres, password)
#参数:发件人,收件人,邮件整体(消息头和消息体的字符串表示)
errs = sendSvr.sendmail(from_addres,to_addres,origmsg)
# 退出服务器
sendSvr.quit()
#,assert返回为假就会触发异常
assert len(errs) == 0, errs
print("QQ邮件发送成功!")
# 睡眠5秒钟,等待邮件被投递,让服务器完成消息的发送和接收。
sleep(5)
# 使用IMAP协议完成邮件的获取,连接服务器
imap_server = imaplib.IMAP4_SSL("imap.qq.com",)
#登录服务器
imap_server.login(from_addres, password)
#选择默认“收件箱”,并打印邮件数量
res, data = imap_server.select('INBOX')
print(res, data)
print(data[0])
#获取最新的一封邮件
typ, lines = imap_server.fetch(data[0], '(RFC822)')
#解析出邮件
msg = email.message_from_string(lines[0][1].decode('utf-8'))
print(msg)
imap_server.close()
3、解析邮件
使用POP3协议获取邮件其实很简单,真正麻烦的是把邮件的原始内容解析为可以阅读的邮件对象。首先使用poplib.POP3或poplib.POP3_SSL按POP3协议从服务器端下载邮件。再使用email.parser.Parser或email.parser.BytesParser解析邮件内容,得到EmailMessage对象,从EmailMessage对象中读取邮件内容。例如:
动手练一练:
import smtplib
import poplib, os.path, mimetypes
from email.mime.text import MIMEText
from email.utils import formataddr
from time import sleep
from email.parser import BytesParser
from email.policy import default
# 发件人邮箱账号
from_addres = "5********8@qq.com"
# 发件人邮箱密码,即配置生成的授权码
password = "m***********b"
# 收件人邮箱账号,你可以填任意想要发送的邮箱,也可以发送给自己
to_addres = "5********8@qq.com"
# html内容
text = """
<h1>学会Python编程,月薪过万不是梦! </h1>
<p><a href="https://www.pyhint.com">在线学习Python编程,进入www.pyhint.com</a></p>
<p>
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。
<img src="cid:img01">
</p>
"""
# 指定消息体使用纯文本格式
message = MIMEText(text, "html", "utf-8")
# 发件人显式的名字
message["From"] = formataddr(["Python编程教学", from_addres])
# 收件人显式的名字
message["To"] = formataddr(["在线编程学习", to_addres])
# 邮件标题
message["Subject"] = "Python解析邮件"
try:
# QQ邮箱SMTP服务器,端口是25
server = smtplib.SMTP_SSL("smtp.qq.com",)
# 登录
server.login(from_addres, password)
# 发送
server.sendmail(from_addres, to_addres, message.as_string())
print("QQ邮件发送成功!")
# 退出服务器
server.quit()
except smtplib.SMTPException as e:
print("QQ邮件发送失败!", e)
# 睡眠5秒钟,等待邮件被投递
sleep(5)
# 连接到POP3服务器:
conn = poplib.POP3_SSL("pop.qq.com",)
# 可以打开或关闭调试信息:
conn.set_debuglevel(1)
# 可选:打印POP 3服务器的欢迎文字:
print(conn.getwelcome().decode('utf-8'))
# 输入用户名
conn.user(from_addres)
# 输入密码,即配置生成的授权码
conn.pass_(password)
# 获取邮件统计信息,相当于发送POP3的stat命令
message_num, total_size = conn.stat()
print('邮件数: %s. 总大小: %s' % (message_num, total_size))
# mails列表保存每封邮件的编号、大小
resp, mails, octets = conn.list()
print(resp, mails)
# 获取指定邮件的内容(此处传入总长度,也就是获取最后一封邮件)
# resp保存服务器的响应码
# data保存该邮件的内容
resp, data, octets = conn.retr(len(mails))
# 将data的所有数据(原本是一个字节列表)拼接在一起
msg_data = b'\r\n'.join(data)
# 将字符串内容解析成邮件,此处一定要指定policy=default
msg = BytesParser(policy=default).parsebytes(msg_data)
print(type(msg))
print('发件人:' + msg['from'])
print('收件人:' + msg['to'])
print('主题:' + msg['subject'])
print('第一个收件人名字:' + msg['to'].addresses[0].username)
print('第一个发件人名字:' + msg['from'].addresses[0].username)
for part in msg.walk():
counter = 1
# 如果maintype是multipart,说明是容器(用于包含正文、附件等)
if part.get_content_maintype() == 'multipart' :
continue
# 如果maintype是multipart,说明是邮件正文部分
elif part.get_content_maintype() == 'text':
print(part.get_content())
# 处理附件
else :
# 获取附件的文件名
filename = part.get_filename()
# 如果没有文件名,程序要负责为附件生成文件名
if not filename:
# 根据附件的contnet_type来推测它的后缀名
ext = mimetypes.guess_extension(part.get_content_type())
# 如果推测不出后缀名
if not ext:
# 使用.bin作为后缀名
ext = '.bin'
# 程序为附件来生成文件名
filename = 'part-%03d%s' % (counter, ext)
counter += 1
# 将附件写入的本地文件
with open(os.path.join('.', filename), 'wb') as fp:
fp.write(part.get_payload(decode=True))
# 退出服务器,相当于发送POP3的quit命令
conn.quit()
上面的例子中,使用smtp发送了一个HTML格式的邮件,睡眠5秒钟后,等待邮件被投递,再使用pop3协议读取了邮件列表,并下载了第一个邮件,如果程序要读取第一个邮件的各部分内容,则需要调用该对象的walk()方法,该方法返回一个可迭代对象,程序使用for循环遍历walk()方法的返回值,对邮件内容进行逐项处理:如果邮件某项的“maintype”是“multipart”,则说明这一项是容器,包含邮件内容、附件等其他项。如果邮件某项的“maintype”是“text”,则说明这一项的内容是文本,通常就是邮件正文或文本附件。对于这种文本内容,程序直接将其输出到控制台中。如果邮件某项的“maintype”是其他,则说明这一项的内容是附件,程序会自动将附件内容保存在本地文件中。
执行以上代码,可以看到程序收取了指定的第一个邮件,并将邮件内容输出到控制台中,将邮件附件保存在本地文件中。