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] 解析邮件传输编码

示例效果图

示例分为两部分,左边是邮件列表,右边是邮件内容,如下所示:

Python如何利用IMAP实现邮箱客户端功能

核心代码

邮件帮助类,主要包括邮件的接收,具体邮件内容的解析等功能,如下所示:


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服务,如下所示:

Python如何利用IMAP实现邮箱客户端功能

如果通过邮箱账户密码登录时,报如下错误,则表示需要通过授权码进行登录,如下所示:

Python如何利用IMAP实现邮箱客户端功能

 温馨提示:在第三方登录QQ邮箱,可能存在邮件泄露风险,甚至危害Apple ID安全,建议使用QQ邮箱手机版登录。

来源:https://blog.csdn.net/fengershishe/article/details/120236253

标签:python,邮箱,客户端
0
投稿

猜你喜欢

  • Python如何通过变量ID得到变量的值

    2023-01-22 22:35:56
  • 网站改版常见问题答疑

    2008-08-22 18:31:00
  • python使用新浪微博api上传图片到微博示例

    2021-10-13 02:15:06
  • MAC 中mysql密码忘记解决办法

    2024-01-18 04:13:48
  • python实现递归查找某个路径下所有文件中的中文字符

    2021-12-11 09:13:18
  • Python中使用haystack实现django全文检索搜索引擎功能

    2023-06-30 07:21:03
  • 十分钟搞定pandas(入门教程)

    2023-08-09 01:00:15
  • 解决SQL Server的“此数据库没有有效所有者”问题

    2011-12-14 18:29:35
  • python遍历迭代器自动链式处理数据的实例代码

    2022-04-12 18:38:29
  • Dreamweaver基础技巧全面接触

    2010-03-25 12:23:00
  • mysql中从字段中URL提取域名信息

    2009-05-25 10:35:00
  • Python判断文件或文件夹是否存在的三种方法

    2021-04-25 21:53:06
  • DDL、DML和DCL的区别与理解

    2024-01-23 23:08:01
  • 一文教你彻底解决Python包下载慢问题

    2023-04-02 20:29:32
  • 在Python中通过threshold创建mask方式

    2022-10-22 16:52:49
  • 使用Python的urllib2模块处理url和图片的技巧两则

    2022-02-15 21:26:00
  • python线程、进程和协程详解

    2023-03-02 14:00:39
  • python实现视频读取和转化图片

    2023-09-25 08:23:22
  • 一个sql查询器,自动画表格填字段

    2011-04-10 10:58:00
  • python正则表达式及使用正则表达式的例子

    2021-07-28 20:15:41
  • asp之家 网络编程 m.aspxhome.com