Python基础知识(35):电子邮件(Ⅱ)

收取邮件就是编写一个MUA作为客户端,从MDA把邮件获取到用户的电脑或者手机上

收取邮件最常用的协议是POP协议,目前版本号是3,俗称POP3

Python内置一个poplib模块,实现了POP3协议,可以直接用来收邮件

1、通过POP3协议下载邮件

获取最新的一封邮件内容

  1. import poplib
  2. from email.parser import Parser
  3.  
  4. #输入邮件地址,口令和POP3服务器地址
  5. email = input('Email: ')
  6. password = input('Password: ')
  7. pop3_server = input('POP3 server: ')
  8.  
  9. #连接到POP3服务器
  10. server = poplib.POP3(pop3_server)
  11. #可以打开或关闭调试信息
  12. server.set_debuglevel(1)
  13. #可选:打印POP3服务器的欢迎文字
  14. print(server.getwelcome().decode('utf-8'))
  15.  
  16. #身份认证
  17. server.user(email)
  18. server.pass_(password)
  19.  
  20. #start()返回邮件数量和占用空间
  21. print('Messages: %s. Size: %s' % server.stat())
  22. #list()返回所有邮件编号
  23. resp, mails, octets = server.list()
  24. #可以查看返回的列表类似[b'1 82923', b'2 2184', ...]
  25. print(mails)
  26.  
  27. #获取最新一封邮件,注意索引号从1开始
  28. index = len(mails)
  29. resp, lines, octets = server.retr(index)
  30.  
  31. #lines存储了邮件的1原始文本的每一行
  32. #可以获得整个邮件的原始文本
  33. msg_content = b'\r\n'.join(lines).decode('utf-8')
  34. #稍后解析出邮件
  35. msg = Parser().parsestr(msg_content)
  36.  
  37. #可以根据邮件索引号直接从服务器删除邮件
  38. #server.dele(index)
  39.  
  40. #关闭连接
  41. server.close()

要获取所有邮件,只需要循环使用retr()把每一封邮件内容拿到即可。真正麻烦的是把邮件的原始内容解析为可以阅读的邮件对象

运行结果如下

  1. Email: xxx@163.com
  2. Password: xxxxx(这里填的是授权码)
  3. POP3 server: pop.163.com
  4. +OK Welcome to coremail Mail Pop3 Server (163coms[b62aaa251425b4be4eaec4ab4744cf47s])
  5. *cmd* 'USER xxx@163.com'
  6. *cmd* 'PASS xxxxx'
  7. *cmd* 'STAT'
  8. *stat* [b'+OK', b'', b'']
  9. Messages: 1. Size: 2375
  10. *cmd* 'LIST'
  11. [b'1 2375']
  12. *cmd* 'RETR 1'

2、解析邮件

解析邮件的过程和SMTP协议构造邮件正好相反,因此,先导入必要的模块

  1. from email.parser import Parser
  2. from email.header import decode_header
  3. from email.utils import parseaddr
  4. import poplib

把邮件内容解析为Message对象

  1. msg = Parser().parsestr(msg_content)

但是这个Message对象本身可能是一个MIMEMultipart对象,即包含嵌套的其他MIMEBase对象,嵌套可能还不止一层。

所以我们要递归地打印出Message对象的层次结构

  1. #indent用于缩进显示
  2. def print_info(msg, indent=0):
  3. if indent == 0:
  4. for header in ['From', 'To', 'Subject']:
  5. value = msg.get(header, '')
  6. if value:
  7. if header == 'Subject':
  8. value = decode_str(value)
  9. else:
  10. hdr, addr = parseaddr(value)
  11. name = decode_str(hdr)
  12. value = u'%s <%s>' % (name,addr)
  13. print('%s%s: %s' % (' ' * indent, header, value))
  14. if (msg.is_multipart()):
  15. parts = msg.get_payload()
  16. for n, part in enumerate(parts):
  17. print('%spart %s' % (' ' * indent, n))
  18. print('%s--------------------' % (' ' * indent))
  19. print_info(part, indent+1)
  20. else:
  21. content_type = msg.get_content_type()
  22. if content_type == 'text/plain' or content_type == 'text/html':
  23. content = msg.get_payload(decode=True)
  24. charset = guess_charset(msg)
  25. if charset:
  26. content = content.decode(charset)
  27. print('%sText: %s' % (' ' * indent, content + '...'))
  28. else:
  29. print('%sAttachemnt: %s' % (' ' * indent, content_type))

邮件的Subject或者Email中包含的名字都是经过编码后的str,要正常显示,就必须decode

  1. def decode_str(s):
  2. value, charset = decode_header(s)[0]
  3. if charset:
  4. value = value.decode(charset)
  5. return value

文本邮件的内容也是str,还需要检测编码,否则,非UTF-8编码的邮件都无法正常显示

  1. def guess_charset(msg):
  2. charset = msg.get_charset()
  3. if charset is None:
  4. content_type = msg.get('Cnotne-Type', '').lower()
  5. pos = content_type.find('charset=')
  6. if pos >= 0:
  7. charset = content_type[pos + 8:].strip()
  8. return charset

完整代码如下:

  1. from email.parser import Parser
  2. from email.header import decode_header
  3. from email.utils import parseaddr
  4. import poplib
  5.  
  6. #输入邮件地址,口令和POP3服务器地址
  7. email = input('Email: ')
  8. password = input('Password: ')
  9. pop3_server = input('POP3 server: ')
  10.  
  11. def decode_str(s):
  12. value, charset = decode_header(s)[0]
  13. if charset:
  14. value = value.decode(charset)
  15. return value
  16.  
  17. def guess_charset(msg):
  18. charset = msg.get_charset()
  19. if charset is None:
  20. content_type = msg.get('Content-Type', '').lower()
  21. pos = content_type.find('charset=')
  22. if pos >= 0:
  23. charset = content_type[pos + 8:].strip()
  24. return charset
  25.  
  26. # indent用于缩进显示:
  27. def print_info(msg, indent=0):
  28. if indent == 0:
  29. for header in ['From', 'To', 'Subject']:
  30. value = msg.get(header, '')
  31. if value:
  32. if header=='Subject':
  33. value = decode_str(value)
  34. else:
  35. hdr, addr = parseaddr(value)
  36. name = decode_str(hdr)
  37. value = u'%s <%s>' % (name, addr)
  38. print('%s%s: %s' % (' ' * indent, header, value))
  39. if (msg.is_multipart()):
  40. parts = msg.get_payload()
  41. for n, part in enumerate(parts):
  42. print('%spart %s' % (' ' * indent, n))
  43. print('%s--------------------' % (' ' * indent))
  44. print_info(part, indent + 1)
  45. else:
  46. content_type = msg.get_content_type()
  47. if content_type=='text/plain' or content_type=='text/html':
  48. content = msg.get_payload(decode=True)
  49. charset = guess_charset(msg)
  50. if charset:
  51. content = content.decode(charset)
  52. print('%sText: %s' % (' ' * indent, content + '...'))
  53. else:
  54. print('%sAttachment: %s' % (' ' * indent, content_type))
  55.  
  56. #连接到POP3服务器
  57. server = poplib.POP3(pop3_server)
  58. #可以打开或关闭调试信息
  59. server.set_debuglevel(1)
  60. #可选:打印POP3服务器的欢迎文字
  61. print(server.getwelcome().decode('utf-8'))
  62.  
  63. #身份认证
  64. server.user(email)
  65. server.pass_(password)
  66.  
  67. #start()返回邮件数量和占用空间
  68. print('Messages: %s. Size: %s' % server.stat())
  69. #list()返回所有邮件编号
  70. resp, mails, octets = server.list()
  71. #可以查看返回的列表类似[b'1 82923', b'2 2184', ...]
  72. print(mails)
  73.  
  74. #获取最新一封邮件,注意索引号从1开始
  75. index = len(mails)
  76. resp, lines, octets = server.retr(index)
  77.  
  78. #lines存储了邮件的1原始文本的每一行
  79. #可以获得整个邮件的原始文本
  80. msg_content = b'\r\n'.join(lines).decode('utf-8')
  81. #稍后解析出邮件
  82. msg = Parser().parsestr(msg_content)
  83. print_info(msg)
  84.  
  85. #关闭连接
  86. server.close()

运行结果如下:

  1. Email: xxx@163.com
  2. Password: xxxxx
  3. POP3 server: pop.163.com
  4. +OK Welcome to coremail Mail Pop3 Server (163coms[b62aaa251425b4be4eaec4ab4744cf47s])
  5. *cmd* 'USER xxx@163.com'
  6. *cmd* 'PASS xxxxx'
  7. *cmd* 'STAT'
  8. *stat* [b'+OK', b'', b'']
  9. Messages: 1. Size: 2375
  10. *cmd* 'LIST'
  11. [b'1 2375']
  12. *cmd* 'RETR 1'
  13. From: xxxx<xxxx@qq.com>
  14. To: xxx <xxx@163.com>
  15. Subject: Hello, world!
  16. part 0
  17. --------------------
  18. Text: ...
  19. part 1
  20. --------------------
  21. Text: <div><br></div><div> </div>...

资料来源:

1、廖雪峰学习官网

2、kornberg_fresnel的博文:https://blog.csdn.net/kornberg_fresnel/article/details/51227761

Python学习之旅(三十六)的更多相关文章

  1. python学习之旅(十六)

    Python基础知识(15):模块 1.可以把模块想象成导入Python以增强其功能的扩展 2.任何程序都可以作为模块导入 3.导入模块并不意味着在导入的时候执行某些操作,它们主要用于定义变量.函数和 ...

  2. Python之路(第三十六篇)并发编程:进程、同步异步、阻塞非阻塞

    一.理论基础 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所有内容都是围绕进程的概念展开的. 即使可以利用的cpu只有一个(早期的 ...

  3. Python学习之旅(十二)

    Python基础知识(11):高级特性 一.分片(切片) 通过索引来获取一定范围内的元素 #字符串 s="Alice" s[0:4:2] 结果: 'Ai' #列表 l=[1,2,3 ...

  4. python接口自动化(三十六)-封装与调用--流程类接口关联续集(详解)

    简介 上一篇已经给大家都介绍过了流程类接口关联,但是由于博客的登录机制改变,所以没有办法给小伙伴们实战演练一下,那么这篇就按照上一篇计划的用jenkins来给小伙伴们演示一下流程类接口的封装和调用,其 ...

  5. 流畅python学习笔记:第十六章:协程

    通常在python进行编程一般都是使用多线程或者多进程来实现.这里介绍另外一种并发的方式,就是协程,但和多线程以及多进程不一样的是,协程是运行在单线程当中的并发.来看下具体的例子: def simpl ...

  6. 【WPF学习】第三十六章 样式基础

    前面三章介绍了WPF资源系统,使用资源可在一个地方定义对象而在整个标记中重用他们.尽管可使用资源存储各种对象,但使用资源最常见的原因之一是通过他们的保存样式. 样式是可应用于元素的属性值集合.WPF样 ...

  7. [转] Linux学习之CentOS(三十六)--FTP服务原理及vsfptd的安装、配置

    本篇随笔将讲解FTP服务的原理以及vsfptd这个最常用的FTP服务程序的安装与配置... 一.FTP服务原理 FTP(File Transfer Protocol)是一个非常古老并且应用十分广泛的文 ...

  8. [ExtJS5学习笔记]第三十六节 报表组件mzPivotGrid

    mzPivotGrid 是一个报表组件,采用这个组件之后,可以令你的应用体现更多的价值. 什么是pivot grid 什么是mzPivotGrid 学习资源 与图表组件的融合 什么是pivot gri ...

  9. Python学习之旅(十九)

    Python基础知识(18):面向对象高级编程(Ⅰ) 使用__slots__:限制实例的属性,只允许实例对类添加某些属性 (1)实例可以随意添加属性 (2)某个实例绑定的方法对另一个实例不起作用 (3 ...

  10. Python学习之旅(十八)

    Python基础知识(17):面向对象编程(Ⅱ) 获取对象信息 在不知道对象信息的情况下,我们想要去获取对象信息,可以使用以下方法 1.type (1)判断对象类型 >>> type ...

随机推荐

  1. Redis接口的调用

    1.hiredis是redis数据库的C接口,目录为/redis-3.2.6/deps/hiredis 2.示例代码如下: #include <stdio.h> #include < ...

  2. python接口自动化测试(七)-unittest-批量用例管理

    我们日常项目中的接口测试案例肯定不止一个,当案例越来越多时我们如何管理这些批量案例?如何保证案例不重复?如果案例非常多(成百上千,甚至更多)时如何保证案例执行的效率?如何做(批量)测试数据的管理?如何 ...

  3. 如何卸载EXCEL中的插件?

    1.问题:每一次启动,excel都会弹出一个提示框,是因为将treeplan给删除了.找不到. 2.解决办法: 1)在加载项界面,点击转到.就进入自己加载的工具界面 2)将相应项前面的勾取消--但某插 ...

  4. 如何永久激活(破解) IntelliJ IDEA 2018.2

    1.去官网下载并安装 idea 地址:https://www.jetbrains.com/idea/download 文件有点大,耐心等待一会儿. 2.下载破解(crack) jar 包 地址 htt ...

  5. 内核中的锁机制--RCU

    一. 引言 众所周知,为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁(rwlock),它们使用起来非常简单,而且是一种很有效的同步机制,在UNIX系统和Linux系统中得到了 ...

  6. Java 连接MongoDB集群的几种方式

    先决条件 先运行mongodb肯定是必须的,然后导入以下包: import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; im ...

  7. 【PHP】PHP的安装和配置

    PHP(外文名:PHP: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,利于学习,使用广泛,主要适用于W ...

  8. 存货控制中的ABC分类释义

    存货控制的ABC制度是根据存货的重要程度把存货归为A.B.C三类,最重要的是A类,最不重要的是C类. A类产品就是指在产品销售进程中,销量比较多,在库存管理方面需要大量备货的产品; B类则是销量适中, ...

  9. CentOS5.5上安装Python2.7及ez_setup和pip包

    CentOS5.5上安装Python2.7及ez_setup和pip包 下载 首先从Python官方下载源代码包下载 编译安装 这里将python安装到/opt/python27文件夹下 tar xv ...

  10. 9-11-Trie树/字典树/前缀树-查找-第9章-《数据结构》课本源码-严蔚敏吴伟民版

    课本源码部分 第9章  查找 - Trie树/字典树/前缀树(键树) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接☛☛☛ <数据结构-C语言版>(严蔚 ...