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  email
  3  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      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,1)">(RFC822) 通过邮箱编号和选择获取数据
 45                     if typ1 ==  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 58                         subject = email.header.decode_header(msg.get('))[0][0].decode(msgCharset)   获取标题并通过标题进行解码
 59  
 60                         Message %s\n%s\n" % (num,subject))   打印输出标题
 61                         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,1)"> 72                             recv_date: recv_date,1)"> 73                             mail_to: mail_to,1)"> 74                             mail_from: mail_from
 75                         }
 76                         email_infos.append(email_info)
 77         else 78             请先初始化并登录 79         return email_infos
 80  
 81      get_email_content(self,num):
 82         content = None
 83         typ1,1)"> 84          85              86             msg = email.message_from_string(data1[0][1].decode( 87             print(msg)
 88              89             msgCharset = email.header.decode_header(msg.get( 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             transfer_encoding:,transfer_encoding)
 93             charset: 94              邮件内容
 95             for part  msg.walk():
 96                 if  part.is_multipart():
 97                     name = part.get_param(name 98                     not name:   如果邮件内容不是附件可以打印输出
 99                         if transfer_encoding == 8bit100                             content = part.get_payload(decode=101                         102                             content = part.get_payload(decode=True).decode(msgCharset)
103  
104             (content)
105             ***********************************end*****************************************106          content
107  
108      get_email_date(self,date):
109         获取时间110         utcstr = date[0][0].replace(+00:00',1)">''111         utcdatetime =112         localtimestamp =113         try114             utcdatetime = datetime.datetime.strptime(utcstr,1)">%a,%d %b %Y %H:%M:%S +0000 (GMT)115             localdatetime = utcdatetime + datetime.timedelta(hours=+8116             localtimestamp = localdatetime.timestamp()
117         except118             119                 utcdatetime = datetime.datetime.strptime(utcstr,%d %b %Y %H:%M:%S +0800 (CST)120                 localtimestamp = utcdatetime.timestamp()
121             122                 utcdatetime = datetime.datetime.strptime(utcstr,%d %b %Y %H:%M:%S +0800123                 localtimestamp =124          localtimestamp
125  
126  
127 if __name__ == __main__128     host = 129     port = 130     username = 131     password =  密码
132     mail_box =  邮箱名
133     eamil_util = EmailUtil(host=host,1)">port)
134     eamil_util.login(username=username,1)">password)
135     eamil_util.get_mail()
136     done')

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

 1 from tkinter import *
 2 from tkinterie.tkinterIE  WebView
 3 from test_email  EmailUtil
 4  time
 5  os
 6  
 7  Application(Frame):
 8     email_util = 9     total_line= 0
10  
11     __init__(self,master=None):
12         ''''''
13         super().__init__(master)   调用父类的初始化方法
14         host = 15         port = 16         username = *********17         password = 18         self.email_util = EmailUtil(host=host,1)">19         self.email_util.login(username=username,1)">20         self.master = master
21          self.pack(side=TOP,fill=BOTH,expand=1)  # 此处填充父窗体
22         self.create_widget()
23  
24      create_widget(self):
25         self.img_logo = PhotoImage(file=logo.png26         self.btn_logo = Button(image=self.img_logo,bg=#222E3C27         self.btn_logo.grid(row=0,column=0,sticky=N + E + W+S)
28          收件箱初始化
29         records = self.email_util.get_mail()
30         for i  range(len(records)):
31              时间特殊处理
32             recv_date =  time.strftime(%Y-%m-%d",time.localtime(records[i][]))
33             subject = {0}   {1}".format(recv_date,records[i][])
34             (subject)
35             num = records[i][36             btn_subject = Button(self.master,text=subject,height=2,width=30,bg=(#F0FFFF" if i%2==0 else #E6E6FA"),anchor=wlambda num=num: self.get_email_content(num) )
37             btn_subject.grid(row=(i + 1),padx=2,pady=138          明细
39         self.total_line=i
40         self.web_view = WebView(self.master,width=530,height=56041         self.web_view.grid(row=0,column=1,rowspan=(i+2),pady=5,sticky=N + E + W)
42  
43     44         获取邮件明细45         content = self.email_util.get_email_content(num)
46         47         if content.find(GBK')>0 or content.find(gbkcnblogs')>0:
48             1-111149              content = content.encode().decode('gbk')
50          print(content)
51         self.save_data(content)
52         abs_path =  os.path.abspath(content.html53         self.web_view= WebView(self.master,height=560,url=file://"+abs_path)
54         self.web_view.grid(row=0,rowspan=(self.total_line + 2),padx=5,1)">55  
56  
57      save_data(self,content):
58         保存数据59         with open() as f:
60             f.write(content)
61  
62  
63 64     root = Tk()
65     root.title(个人邮箱66     root.geometry(760x580+200+20067     root.setvar(bgred68     app = Application(master=root)
69     root.mainloop()

邮箱设置

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

 

 

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

 

 

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

备注

逢雪宿芙蓉山主人

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

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

 

原文地址:https://www.cnblogs.com/hsiang

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Python中的函数(二) 在上一篇文章中提到了Python中函数的定义和使用,在这篇文章里我们来讨论下关于函数的一些更深的话题。在学习C语言函数的时候,遇到的问题主要有形参实参的区别、参数的传递和改变、变量的作用域。同样在Python中,关于对函数的理解和使用也存在这些问题。下面来逐一讲解。一.函
Python中的字符串 可能大多数人在学习C语言的时候,最先接触的数据类型就是字符串,因为大多教程都是以"Hello world"这个程序作为入门程序,这个程序中要打印的"Hello world"就是字符串。如果你做过自然语言处理方面的研究,并且用Python
Python 面向对象编程(一) 虽然Python是解释性语言,但是它是面向对象的,能够进行对象编程。下面就来了解一下如何在Python中进行对象编程。一.如何定义一个类 在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法。 类是对现实世界中一些事物的封装,
Python面向对象编程(二) 在前面一篇文章中谈到了类的基本定义和使用方法,这只体现了面向对象编程的三大特点之一:封装。下面就来了解一下另外两大特征:继承和多态。 在Python中,如果需要的话,可以让一个类去继承一个类,被继承的类称为父类或者超类、也可以称作基类,继承的类称为子类。并且Pytho
Python中的函数(一) 接触过C语言的朋友对函数这个词肯定非常熟悉,无论在哪门编程语言当中,函数(当然在某些语言里称作方法,意义是相同的)都扮演着至关重要的角色。今天就来了解一下Python中的函数用法。一.函数的定义 在某些编程语言当中,函数声明和函数定义是区分开的(在这些编程语言当中函数声明
在windows下如何快速搭建web.py开发框架 用Python进行web开发的话有很多框架供选择,比如最出名的Django,tornado等,除了这些框架之外,有一个轻量级的框架使用起来也是非常方便和顺手,就是web.py。它由一名黑客所创建,但是不幸的是这位创建者于2013年自杀了。据说现在由
将Sublime Text 2搭建成一个好用的IDE 说起编辑器,可能大部分人要推荐的是Vim和Emacs,本人用过Vim,功能确实强大,但是不是很习惯,之前一直有朋友推荐SUblime Text 2这款编辑器,然后这段时间就试了一下,就深深地喜欢上这款编辑器了...
Python中的模块 有过C语言编程经验的朋友都知道在C语言中如果要引用sqrt这个函数,必须用语句"#include<math.h>"引入math.h这个头文件,否则是无法正常进行调用的。那么在Python中,如果要引用一些内置的函数,该怎么处理呢?在Python中
Python的基础语法 在对Python有了基础的认识之后,下面来了解一下Python的基础语法,看看它和C语言、java之间的基础语法差异。一.变量、表达式和语句 Python中的语句也称作命令,比如print "hello python"这就是一条语句。 表达式,顾名思义,是
Eclipse+PyDevʽjango+Mysql搭建Python web开发环境 Python的web框架有很多,目前主流的有Django、Tornado、Web.py等,最流行的要属Django了,也是被大家最看好的框架之一。下面就来讲讲如何搭建Django的开发环境。一.准备工作 需要下载的
在windows下安装配置Ulipad 今天推荐一款轻便的文本编辑器Ulipad,用来写一些小的Python脚本非常方便。 Ulipad下载地址: https://github.com/limodou/ulipad http://files.cnblogs.com/dolphin0520/u...
Python中的函数(三) 在前面两篇文章中已经探讨了函数的一些相关用法,下面一起来了解一下函数参数类型的问题。在C语言中,调用函数时必须依照函数定义时的参数个数以及类型来传递参数,否则将会发生错误,这个是严格进行规定的。然而在Python中函数参数定义和传递的方式相比而言就灵活多了。一.函数参数的
在Notepad++中搭配Python开发环境 Python在最近几年一度成为最流行的语言之一,不仅仅是因为它简洁明了,更在于它的功能之强大。它不仅能够完成一般脚本语言所能做的事情,还能很方便快捷地进行大规模的项目开发。在学习Python之前我们来看一下Python的历史由来,"Pytho
Python中的条件选择和循环语句 同C语言、Java一样,Python中也存在条件选择和循环语句,其风格和C语言、java的很类似,但是在写法和用法上还是有一些区别。今天就让我们一起来了解一下。一.条件选择语句 Python中条件选择语句的关键字为:if 、elif 、else这三个。其基本形式如
关于raw_input( )和sys.stdin.readline( )的区别 之前一直认为用raw_input( )和sys.stdin.readline( )来获取输入的效果完全相同,但是最近在写程序时有类似这样一段代码:import sysline = sys.stdin.readline()
初识Python 跟学习所有的编程语言一样,首先得了解这门语言的编程风格和最基础的语法。下面就让我们一起来了解一下Python的编程风格。1.逻辑行与物理行 在Python中有逻辑行和物理行这个概念,物理行是指在编辑器中实际看到的一行,逻辑行是指一条Python语句。在Python中提倡一个物理行只
当我们的代码是有访问网络相关的操作时,比如http请求或者访问远程数据库,经常可能会发生一些错误,有些错误可能重新去发送请求就会成功,本文分析常见可能需要重试的场景,并最后给出python代码实现。
1.经典迭代器 2.将Sentence中的__iter__改成生成器函数 改成生成器后用法不变,但更加简洁。 3.惰性实现 当列表比较大,占内存较大时,我们可以采用惰性实现,每次只读取一个元素到内存。 或者使用更简洁的生成器表达式 4.yield from itertools模块含有大量生成器函数可
本文介绍简单介绍socket的常用函数,并以python-kafka中的源码socketpair为例,来讲解python socket的运用
python实践中经常出现编码相关的异常,大多网上找资料而没有理解原理,导致一次次重复错误。本文对常用Unicode、UTF-8、GB2312编码的原理进行介绍,接着介绍了python字符类型unicode和str以及常见编解码错误UnicodeEncodeError和UnicodeDEcodeEr