zoukankan      html  css  js  c++  java
  • Python通过IMAP实现邮箱客户端

    概述

    在日常工作生活中,都是利用个人或公司的邮箱客户端进行收发邮件,那么如何打造一款属于自己的邮箱客户端呢?本文以一个简单的小例子,简述如何通过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] 解析邮件传输编码

    示例效果图

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

     

    核心代码

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

      1 import imaplib
      2 import email
      3 import datetime
      4  
      5  
      6 class EmailUtil:
      7     """
      8     Email帮助类
      9     """
     10     host = 'imap.qq.com'  # 主机IP或者域名
     11     port = '993'  # 端口
     12     username = '********'  # 用户名
     13     password = '**************'  # 密码或授权码
     14     imap = None  # 邮箱连接对象
     15  
     16     # mail_box = '**************'  # 邮箱名
     17  
     18     def __init__(self, host, port):
     19         """初始化方法"""
     20         self.host = host
     21         self.port = port
     22         # 初始化一个邮箱链接对象
     23         self.imap = imaplib.IMAP4_SSL(host=self.host, port=int(self.port))
     24  
     25     def login(self, username, password):
     26         """登录"""
     27         self.username = username
     28         self.password = password
     29         self.imap.login(user=self.username, password=self.password)
     30  
     31     def get_mail(self):
     32         """获取邮件"""
     33         # self.mail_box = mail_box
     34         email_infos = []
     35         if self.imap is not None:
     36             self.imap.select(readonly=False)
     37             typ, data = self.imap.search(None, 'ALL')  # 返回一个元组,data为此邮箱的所有邮件数据
     38             #  数据格式 data =  [b'1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18']
     39             if typ == 'OK':
     40                 for num in data[0].split():
     41                     if int(num) > 10:
     42                         # 超过20,退出循环,不输出
     43                         break
     44                     typ1, data1 = self.imap.fetch(num, '(RFC822)')  # 通过邮箱编号和选择获取数据
     45                     if typ1 == 'OK':
     46                         print('**********************************begin******************************************')
     47                         msg = email.message_from_string(data1[0][1].decode("utf-8"))  # 用email库获取解析数据(消息体)
     48                         # 获取邮件标题并进行进行解码,通过返回的元组的第一个元素我们得知消息的编码
     49                         msgCharset = email.header.decode_header(msg.get('Subject'))[0][1]
     50                         # print('msg = ',msg)
     51                         # print('msgCharset= ',msgCharset)  # gb2312
     52                         recv_date = self.get_email_date(email.header.decode_header(msg.get('Date')))
     53                         mail_from = email.header.decode_header(msg.get('From'))[0][0]
     54                         if type(mail_from) == bytes:
     55                             mail_from = mail_from.decode(msgCharset)
     56  
     57                         mail_to = email.header.decode_header(msg.get('To'))[0][0]
     58                         subject = email.header.decode_header(msg.get('Subject'))[0][0].decode(msgCharset)  # 获取标题并通过标题进行解码
     59  
     60                         print("Message %s
    %s
    " % (num, subject))  # 打印输出标题
     61                         print('mail_from:' + mail_from + ' mail_to:' + mail_to + ' recv_date:' + str(recv_date))
     62                         # # 邮件内容
     63                         # for part in msg.walk():
     64                         #     if not part.is_multipart():
     65                         #         name = part.get_param("name")
     66                         #         if not name:  # 如果邮件内容不是附件可以打印输出
     67                         #             print(part.get_payload(decode=True).decode(msgCharset))
     68                         # print('***********************************end*****************************************')
     69                         email_info = {
     70                             "num": num,
     71                             "subject": subject,
     72                             "recv_date": recv_date,
     73                             "mail_to": mail_to,
     74                             "mail_from": mail_from
     75                         }
     76                         email_infos.append(email_info)
     77         else:
     78             print('请先初始化并登录')
     79         return email_infos
     80  
     81     def get_email_content(self, num):
     82         content = None
     83         typ1, data1 = self.imap.fetch(num, '(RFC822)')  # 通过邮箱编号和选择获取数据
     84         if typ1 == 'OK':
     85             print('**********************************begin******************************************')
     86             msg = email.message_from_string(data1[0][1].decode("utf-8"))  # 用email库获取解析数据(消息体)
     87             print(msg)
     88             # 获取邮件标题并进行进行解码,通过返回的元组的第一个元素我们得知消息的编码
     89             msgCharset = email.header.decode_header(msg.get('Subject'))[0][1]
     90             # transfer_encoding = email.header.decode_header(msg.get('Content-Transfer-Encoding'))
     91             transfer_encoding = email.utils.parseaddr(msg.get('Content-Transfer-Encoding'))[1]
     92             print("transfer_encoding:",transfer_encoding)
     93             print("charset:",msgCharset)
     94             # 邮件内容
     95             for part in msg.walk():
     96                 if not part.is_multipart():
     97                     name = part.get_param("name")
     98                     if not name:  # 如果邮件内容不是附件可以打印输出
     99                         if transfer_encoding == '8bit':
    100                             content = part.get_payload(decode=False)
    101                         else:
    102                             content = part.get_payload(decode=True).decode(msgCharset)
    103  
    104             print(content)
    105             print('***********************************end*****************************************')
    106         return content
    107  
    108     def get_email_date(self, date):
    109         """获取时间"""
    110         utcstr = date[0][0].replace('+00:00', '')
    111         utcdatetime = None
    112         localtimestamp = None
    113         try:
    114             utcdatetime = datetime.datetime.strptime(utcstr, '%a, %d %b %Y %H:%M:%S +0000 (GMT)')
    115             localdatetime = utcdatetime + datetime.timedelta(hours=+8)
    116             localtimestamp = localdatetime.timestamp()
    117         except:
    118             try:
    119                 utcdatetime = datetime.datetime.strptime(utcstr, '%a, %d %b %Y %H:%M:%S +0800 (CST)')
    120                 localtimestamp = utcdatetime.timestamp()
    121             except:
    122                 utcdatetime = datetime.datetime.strptime(utcstr, '%a, %d %b %Y %H:%M:%S +0800')
    123                 localtimestamp = utcdatetime.timestamp()
    124         return localtimestamp
    125  
    126  
    127 if __name__ == '__main__':
    128     host = 'imap.qq.com'  # 主机IP或者域名
    129     port = '993'  # 端口
    130     username = '********'  # 用户名
    131     password = '**************'  # 密码
    132     mail_box = '**************'  # 邮箱名
    133     eamil_util = EmailUtil(host=host, port=port)
    134     eamil_util.login(username=username, password=password)
    135     eamil_util.get_mail()
    136     print('done')

    邮件展示类,主要用于邮件内容在前台页面的展示,如下所示:

     1 from tkinter import *
     2 from tkinterie.tkinterIE import WebView
     3 from test_email import EmailUtil
     4 import time
     5 import os
     6  
     7 class Application(Frame):
     8     email_util = None
     9     total_line= 0
    10  
    11     def __init__(self, master=None):
    12         '''初始化方法'''
    13         super().__init__(master)  # 调用父类的初始化方法
    14         host = 'imap.qq.com'  # 主机IP或者域名
    15         port = '993'  # 端口
    16         username = '*********'  # 用户名
    17         password = '**************'  # 密码或授权码
    18         self.email_util = EmailUtil(host=host, port=port)
    19         self.email_util.login(username=username, password=password)
    20         self.master = master
    21         # self.pack(side=TOP, fill=BOTH, expand=1)  # 此处填充父窗体
    22         self.create_widget()
    23  
    24     def create_widget(self):
    25         self.img_logo = PhotoImage(file="logo.png")
    26         self.btn_logo = Button(image=self.img_logo , bg='#222E3C')
    27         self.btn_logo.grid(row=0, column=0, sticky=N + E + W+S)
    28         # 收件箱初始化
    29         records = self.email_util.get_mail()
    30         for i in range(len(records)):
    31             # 时间特殊处理
    32             recv_date =  time.strftime("%Y-%m-%d", time.localtime(records[i]["recv_date"]))
    33             subject = "{0}   {1}".format(recv_date, records[i]["subject"])
    34             print(subject)
    35             num = records[i]["num"]
    36             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) )
    37             btn_subject.grid(row=(i + 1), column=0, padx=2, pady=1)
    38         # 明细
    39         self.total_line=i
    40         self.web_view = WebView(self.master, width=530, height=560)
    41         self.web_view.grid(row=0, column=1, rowspan=(i+2), padx=2, pady=5, sticky=N + E + W)
    42  
    43     def get_email_content(self,num):
    44         """获取邮件明细"""
    45         content = self.email_util.get_email_content(num)
    46         print(content)
    47         if content.find('GBK')>0 or content.find('gbk')>0 or content.find('cnblogs')>0:
    48             print('1-1111')
    49             # content = content.encode().decode('gbk')
    50         # print(content)
    51         self.save_data(content)
    52         abs_path =  os.path.abspath("content.html")
    53         self.web_view= WebView(self.master, width=530, height=560,url="file://"+abs_path)
    54         self.web_view.grid(row=0, column=1, rowspan=(self.total_line + 2), padx=5, pady=5, sticky=N + E + W)
    55  
    56  
    57     def save_data(self,content):
    58         """保存数据"""
    59         with open('content.html', 'w', encoding='utf-8') as f:
    60             f.write(content)
    61  
    62  
    63 if __name__ == '__main__':
    64     root = Tk()
    65     root.title('个人邮箱')
    66     root.geometry('760x580+200+200')
    67     root.setvar("bg", "red")
    68     app = Application(master=root)
    69     root.mainloop()

    邮箱设置

    如果要使用IMAP协议访问邮箱服务进行收发邮件,则必须进行邮箱设置,路径:登录邮箱-->设置-->账户-->POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务,如下所示:

     

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

     

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

    备注

    逢雪宿芙蓉山主人

    【作者】刘长卿【朝代】唐

    日暮苍山远,天寒白屋贫。柴门闻犬吠,风雪夜归人。


    作者:Alan.hsiang
    出处:http://www.cnblogs.com/hsiang/
    本文版权归作者和博客园共有,写文不易,支持原创,欢迎转载【点赞】,转载请保留此段声明,且在文章页面明显位置给出原文连接,谢谢。
    关注个人公众号,定时同步更新技术及职场文章

  • 相关阅读:
    算法训练 表达式计算
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    New ways to verify that Multipath TCP works through your network
    TCP的拥塞控制 (Tahoe Reno NewReno SACK)
    Multipath TCP Port for Android 4.1.2
    How to enable ping response in windows 7?
    NS3
    Multipath TCP Port for Android
  • 原文地址:https://www.cnblogs.com/hsiang/p/15306652.html
Copyright © 2011-2022 走看看