Python如何利用IMAP实现邮箱客户端功能
作者:Alan.hsiang 发布时间:2023-11-08 22:23:56
概述
在日常工作生活中,都是利用个人或公司的邮箱客户端进行收发邮件,那么如何打造一款属于自己的邮箱客户端呢?本文以一个简单的小例子,简述如何通过Pyhton的imaplib和email两大模块,实现邮件的接收并展示,仅供学习分享使用,如有不足之处,还请指正。
什么是IMAP?
IMAP,即Internet Message Access Protocol(互联网邮件访问协议),您可以通过这种协议从邮件服务器上获取邮件的信息、下载邮件等。IMAP与POP类似,都是一种邮件获取协议。
IMAP和POP有什么区别?
POP允许电子邮件客户端下载服务器上的邮件,但是您在电子邮件客户端的操作(如:移动邮件、标记已读等),这是不会反馈到服务器上的,比如:您通过电子邮件客户端收取了QQ邮箱中的3封邮件并移动到了其他文件夹,这些移动动作是不会反馈到服务器上的,也就是说,QQ邮箱服务器上的这些邮件是没有同时被移动的 。但是IMAP就不同了,电子邮件客户端的操作都会反馈到服务器上,您对邮件进行的操作(如:移动邮件、标记已读等),服务器上的邮件也会做相应的动作。也就是说,IMAP是“双向”的。
同时,IMAP可以只下载邮件的主题,只有当您真正需要的时候,才会下载邮件的所有内容。
如何设置IMAP服务的SSL加密方式?
使用SSL的通用配置如下:
接收邮件服务器:imap.qq.com,使用SSL,端口号993
发送邮件服务器:smtp.qq.com,使用SSL,端口号465或587
账户名:您的QQ邮箱账户名(如果您是VIP帐号或Foxmail帐号,账户名需要填写完整的邮件地址)
密码:您的QQ邮箱密码
电子邮件地址:您的QQ邮箱的完整邮件地址
涉及知识点
在本示例中,涉及知识点如下所示:
imaplib模块:此模块实现通过IMAP【Internet Message Access Protocol,信息交互访问协议】协议进行邮箱的登录,接收和发送等功能。
IMAP4_SSL(host='', port=IMAP4_SSL_PORT),通过此方法可以定义一个IMAP对象,需要对应的服务器和端口号。
login(self, user, password),通过此方法实现对应邮箱的登录,传入指定的账号,密码即可。
select(self, mailbox='INBOX', readonly=False) 选择收件箱
search(self, charset, *criteria) 查找获取邮箱数据
fetch(self, message_set, message_parts) 通过邮件编号,查找具体的邮件内容
email模块:此模块主要用于邮件的解析功能
message_from_string(s, *args, **kws) , 获取解析数据消息体
email.header.decode_header(msg.get('Subject'))[0][1] 解析编码方式
email.header.decode_header(msg.get('Date')) 解析邮件接收时间
email.header.decode_header(msg.get('From'))[0][0] 解析发件人
email.header.decode_header(msg.get('Subject'))[0][0].decode(msgCharset) 解析邮件标题
email.utils.parseaddr(msg.get('Content-Transfer-Encoding'))[1] 解析邮件传输编码
示例效果图
示例分为两部分,左边是邮件列表,右边是邮件内容,如下所示:
核心代码
邮件帮助类,主要包括邮件的接收,具体邮件内容的解析等功能,如下所示:
import imaplib
import email
import datetime
class EmailUtil:
"""
Email帮助类
"""
host = 'imap.qq.com' # 主机IP或者域名
port = '993' # 端口
username = '********' # 用户名
password = '**************' # 密码或授权码
imap = None # 邮箱连接对象
# mail_box = '**************' # 邮箱名
def __init__(self, host, port):
"""初始化方法"""
self.host = host
self.port = port
# 初始化一个邮箱链接对象
self.imap = imaplib.IMAP4_SSL(host=self.host, port=int(self.port))
def login(self, username, password):
"""登录"""
self.username = username
self.password = password
self.imap.login(user=self.username, password=self.password)
def get_mail(self):
"""获取邮件"""
# self.mail_box = mail_box
email_infos = []
if self.imap is not None:
self.imap.select(readonly=False)
typ, data = self.imap.search(None, 'ALL') # 返回一个元组,data为此邮箱的所有邮件数据
# 数据格式 data = [b'1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18']
if typ == 'OK':
for num in data[0].split():
if int(num) > 10:
# 超过20,退出循环,不输出
break
typ1, data1 = self.imap.fetch(num, '(RFC822)') # 通过邮箱编号和选择获取数据
if typ1 == 'OK':
print('**********************************begin******************************************')
msg = email.message_from_string(data1[0][1].decode("utf-8")) # 用email库获取解析数据(消息体)
# 获取邮件标题并进行进行解码,通过返回的元组的第一个元素我们得知消息的编码
msgCharset = email.header.decode_header(msg.get('Subject'))[0][1]
# print('msg = ',msg)
# print('msgCharset= ',msgCharset) # gb2312
recv_date = self.get_email_date(email.header.decode_header(msg.get('Date')))
mail_from = email.header.decode_header(msg.get('From'))[0][0]
if type(mail_from) == bytes:
mail_from = mail_from.decode(msgCharset)
mail_to = email.header.decode_header(msg.get('To'))[0][0]
subject = email.header.decode_header(msg.get('Subject'))[0][0].decode(msgCharset) # 获取标题并通过标题进行解码
print("Message %s\n%s\n" % (num, subject)) # 打印输出标题
print('mail_from:' + mail_from + ' mail_to:' + mail_to + ' recv_date:' + str(recv_date))
# # 邮件内容
# for part in msg.walk():
# if not part.is_multipart():
# name = part.get_param("name")
# if not name: # 如果邮件内容不是附件可以打印输出
# print(part.get_payload(decode=True).decode(msgCharset))
# print('***********************************end*****************************************')
email_info = {
"num": num,
"subject": subject,
"recv_date": recv_date,
"mail_to": mail_to,
"mail_from": mail_from
}
email_infos.append(email_info)
else:
print('请先初始化并登录')
return email_infos
def get_email_content(self, num):
content = None
typ1, data1 = self.imap.fetch(num, '(RFC822)') # 通过邮箱编号和选择获取数据
if typ1 == 'OK':
print('**********************************begin******************************************')
msg = email.message_from_string(data1[0][1].decode("utf-8")) # 用email库获取解析数据(消息体)
print(msg)
# 获取邮件标题并进行进行解码,通过返回的元组的第一个元素我们得知消息的编码
msgCharset = email.header.decode_header(msg.get('Subject'))[0][1]
# transfer_encoding = email.header.decode_header(msg.get('Content-Transfer-Encoding'))
transfer_encoding = email.utils.parseaddr(msg.get('Content-Transfer-Encoding'))[1]
print("transfer_encoding:",transfer_encoding)
print("charset:",msgCharset)
# 邮件内容
for part in msg.walk():
if not part.is_multipart():
name = part.get_param("name")
if not name: # 如果邮件内容不是附件可以打印输出
if transfer_encoding == '8bit':
content = part.get_payload(decode=False)
else:
content = part.get_payload(decode=True).decode(msgCharset)
print(content)
print('***********************************end*****************************************')
return content
def get_email_date(self, date):
"""获取时间"""
utcstr = date[0][0].replace('+00:00', '')
utcdatetime = None
localtimestamp = None
try:
utcdatetime = datetime.datetime.strptime(utcstr, '%a, %d %b %Y %H:%M:%S +0000 (GMT)')
localdatetime = utcdatetime + datetime.timedelta(hours=+8)
localtimestamp = localdatetime.timestamp()
except:
try:
utcdatetime = datetime.datetime.strptime(utcstr, '%a, %d %b %Y %H:%M:%S +0800 (CST)')
localtimestamp = utcdatetime.timestamp()
except:
utcdatetime = datetime.datetime.strptime(utcstr, '%a, %d %b %Y %H:%M:%S +0800')
localtimestamp = utcdatetime.timestamp()
return localtimestamp
if __name__ == '__main__':
host = 'imap.qq.com' # 主机IP或者域名
port = '993' # 端口
username = '********' # 用户名
password = '**************' # 密码
mail_box = '**************' # 邮箱名
eamil_util = EmailUtil(host=host, port=port)
eamil_util.login(username=username, password=password)
eamil_util.get_mail()
print('done')
邮件展示类,主要用于邮件内容在前台页面的展示,如下所示:
from tkinter import *
from tkinterie.tkinterIE import WebView
from test_email import EmailUtil
import time
import os
class Application(Frame):
email_util = None
total_line= 0
def __init__(self, master=None):
'''初始化方法'''
super().__init__(master) # 调用父类的初始化方法
host = 'imap.qq.com' # 主机IP或者域名
port = '993' # 端口
username = '*********' # 用户名
password = '**************' # 密码或授权码
self.email_util = EmailUtil(host=host, port=port)
self.email_util.login(username=username, password=password)
self.master = master
# self.pack(side=TOP, fill=BOTH, expand=1) # 此处填充父窗体
self.create_widget()
def create_widget(self):
self.img_logo = PhotoImage(file="logo.png")
self.btn_logo = Button(image=self.img_logo , bg='#222E3C')
self.btn_logo.grid(row=0, column=0, sticky=N + E + W+S)
# 收件箱初始化
records = self.email_util.get_mail()
for i in range(len(records)):
# 时间特殊处理
recv_date = time.strftime("%Y-%m-%d", time.localtime(records[i]["recv_date"]))
subject = "{0} {1}".format(recv_date, records[i]["subject"])
print(subject)
num = records[i]["num"]
btn_subject = Button(self.master, text=subject,height=2, width=30, bg=("#F0FFFF" if i%2==0 else "#E6E6FA"), anchor='w',command=lambda num=num: self.get_email_content(num) )
btn_subject.grid(row=(i + 1), column=0, padx=2, pady=1)
# 明细
self.total_line=i
self.web_view = WebView(self.master, width=530, height=560)
self.web_view.grid(row=0, column=1, rowspan=(i+2), padx=2, pady=5, sticky=N + E + W)
def get_email_content(self,num):
"""获取邮件明细"""
content = self.email_util.get_email_content(num)
print(content)
if content.find('GBK')>0 or content.find('gbk')>0 or content.find('cnblogs')>0:
print('1-1111')
# content = content.encode().decode('gbk')
# print(content)
self.save_data(content)
abs_path = os.path.abspath("content.html")
self.web_view= WebView(self.master, width=530, height=560,url="file://"+abs_path)
self.web_view.grid(row=0, column=1, rowspan=(self.total_line + 2), padx=5, pady=5, sticky=N + E + W)
def save_data(self,content):
"""保存数据"""
with open('content.html', 'w', encoding='utf-8') as f:
f.write(content)
if __name__ == '__main__':
root = Tk()
root.title('个人邮箱')
root.geometry('760x580+200+200')
root.setvar("bg", "red")
app = Application(master=root)
root.mainloop()
邮箱设置
如果要使用IMAP协议访问邮箱服务进行收发邮件,则必须进行邮箱设置,路径:登录邮箱-->设置-->账户-->POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务,如下所示:
如果通过邮箱账户密码登录时,报如下错误,则表示需要通过授权码进行登录,如下所示:
温馨提示:在第三方登录QQ邮箱,可能存在邮件泄露风险,甚至危害Apple ID安全,建议使用QQ邮箱手机版登录。
来源:https://blog.csdn.net/fengershishe/article/details/120236253
猜你喜欢
- 在MySQL经历了2008年Sun的收购和2009年Oracle收购Sun的过程中,基本处于停滞发展的情况,在可以预见的未来,MySQL是肯
- 一、分工和流程在土豆网,以项目开发为核心,谁都可以带项目,担任项目经理。一个典型的土豆网项目中,当进入正式开发阶段,通常参与者包括:1名设计
- 本文实例讲述了Python反射和内置方法重写操作。分享给大家供大家参考,具体如下:isinstance和issubclassisinstan
- 无参数函数先解释一下时间戳,所谓时间戳,即自1970年1月1日00:00:00所经历的秒数,然后就可以理解下面的函数了。下面代码默认from
- 行高的概念看上去很简单——文字行的高度,其实,行高所涉及到的基础知识,对于今后理解其它属性也很重要。大片密密麻麻的文字往往会让人觉得乏味,因
- 题目描述1275. 找出井字棋的获胜者 - 力扣(LeetCode)A 和 B 在一个 3 x&nb
- 一、正则表达式的作用提示:正则表达式是一段特殊的字符串,它表示的是一段有规律的信息。如果我们想从一段文字中提取想要的内容,就可以通过正则表达
- 本文实例为大家分享了python绘制汉诺塔的具体代码,供大家参考,具体内容如下源码:import turtleclass Stack: &n
- 这篇文章将会为大家介绍GoFrame gset使用入门,为了让大家更好的理解,会简明扼要的分析一下集合类型Set的特点,对比一下Java、P
- 文章所罗列的问题虽然看似简单,但是每个背后都涵盖了一个或几个大家容易忽视的基础知识点,希望能够帮助到你的面试和平时工作。Q1第一个问题关于弱
- 元素是文档结构的基础,在CSS中,每个元素生成了一个包含了元素内容的框(box,也译为“盒子”)。但是不同的元素显示的方式会有所不同,例如&
- sort 包源码解读前言我们的代码业务中很多地方需要我们自己进行排序操作,go 标准库中是提供了 sort 包是实现排序功能的,这里来看下生
- 如何用javascript来判定选择单选的radio哪个被选择,多选的checkbox有那几个被选中? <!DOCTYPE
- 代码如下:USE TestDB declare @conversation uniqueidentifier while exists (s
- 可怜我的C盘本来只有8.XG,所以不得不卸载掉它。卸载掉本身没啥问题,只是昨晚突然发现 Sql Server 2008 R2 Managem
- 1、Linux主机重定向 Godaddy的Liunx主机,Godaddy本身已经支持Apache,所以直接创建一个.htaccess文件就可
- 品牌是我们一直挂在嘴边的词语,视觉设计师们经常说到,公司的品牌该如何如何去设计?这个违背了我们的公司品牌!等等。之前我有谈过关于 品牌灵魂的
- 简化了一下 YUI3 中的沙箱实现 方式:if (typeof Sandbox === 'undefined' || !Sa
- 本文实例讲述了PHP解析xml格式数据工具类。分享给大家供大家参考,具体如下:class ome_xml { /**  
- 本文实例讲述了JavaScript使用正则表达式获取全部分组内容的方法。分享给大家供大家参考,具体如下:1. 需要使用正则表达式的exec2