Python学习笔记(四十八)POP3收取邮件
收取邮件就是编写一个MUA作为客户端,从MDA把邮件获取到用户的电脑或者手机上。收取邮件最常用的协议是POP协议,目前版本号是3,俗称POP3。
Python内置一个poplib模块,实现了POP3协议,可以直接用来收邮件。
注意到POP3协议收取的不是一个已经可以阅读的邮件本身,而是邮件的原始文本,这和SMTP协议很像,SMTP发送的也是经过编码后的一大段文本。
要把POP3收取的文本变成可以阅读的邮件,还需要用email模块提供的各种类来解析原始文本,变成可阅读的邮件对象。
所以,收取邮件分两步:
第一步:用poplib把邮件的原始文本下载到本地; 第二部:用email解析原始文本,还原为邮件对象。
通过POP3下载邮件
POP3协议本身很简单,以下面的代码为例,我们来获取最新的一封邮件内容:
import poplib # 输入邮件地址, 口令和POP3服务器地址:
from email.parser import Parser email = input('Email:')
password = input('Password:')
pop3_server = input('POP3 server:') # 连接到POP3服务器
server = poplib.POP3(pop3_server)
# 可以打开或关闭调试信息:
server.set_debuglevel(1)
# 可选: 打印POP3服务器的欢迎文字:
print(server.getwelcome().decode('utf-8')) # 身份认证:
server.user(email)
server.pass_(password) # stat() 返回邮件数量和占用空间:
print('Message: %s. Size: %s' % server.stat())
# list()返回所有邮件的编号:
resp, mails, octets = server.list()
# 可以查看返回的列表类似[b'1 82923', b'2 2184', ....]
print(mails) # 获取最新一封邮件, 注意索引号从1开始:
index = len(mails)
resp, lines, octets = server.retr(index) # lines存储了邮件的原始文本的每一行,
# 可以获得整个邮件的原始文本:
msg_content = b'\r\n'.join(lines).decode('utf-8')
# 稍后解析出邮件:
msg = Parser().parsestr(msg_content) # 可以根据邮件索引号直接从服务器删除邮件:
# server.dele(index)
# 关闭连接:
server.quit()
用POP3获取邮件其实很简单,要获取所有邮件,只需要循环使用retr()把每一封邮件内容拿到即可。真正麻烦的是把邮件的原始内容解析为可以阅读的邮件对象。
解析邮件
解析邮件的过程和上一节构造邮件正好相反,因此,先导入必要的模块:
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr import poplib
只需要一行代码就可以把邮件内容解析为Message对象:
msg = Parser().parsestr(msg_content)
但是这个Message对象本身可能是一个MIMEMultipart对象,即包含嵌套的其他MIMEBase对象,嵌套可能还不止一层。
所以我们要递归地打印出Message对象的层次结构:
# 递归地打印出Message对象的层次结构:
# indent用于缩进显示:
def print_info(msg, indent=0):
if indent == 0:
for header in ['From', 'To', 'Subject']:
value = msg.get(header, '')
if value:
if header == 'Subject':
value = decode_str(value)
else:
hdr, addr = parseaddr(value)
name = decode_str(hdr)
value = u'%s <%s>' % (name, addr)
print('%s%s: %s' % (' ' * indent, header, value))
if (msg.is_multipart()):
parts = msg.get_payload()
for n, part in enumerate(parts):
print('%spart %s' % (' ' * indent, n))
print('%s-------------------' % (' ' * indent))
print_info(part, indent + 1)
else:
content_type = msg.get_content_type()
if content_type=='text/plain' or content_type=='text/html':
content = msg.get_payload(decode=True)
charset = guess_charset(msg)
if charset:
content = content.decode(charset)
print('%sText: %s' % (' ' * indent, content + '...'))
else:
print('%sAttachment: %s' % (' ' * indent, content_type))
邮件的Subject或者Email中包含的名字都是经过编码后的str,要正常显示,就必须decode:
def decode_str(s):
value, charset = decode_header(s)[0]
if charset:
value = value.decode(charset)
return value
decode_header()返回一个list,因为像Cc、Bcc这样的字段可能包含多个邮件地址,所以解析出来的会有多个元素。上面的代码我们偷了个懒,只取了第一个元素。
文本邮件的内容也是str,还需要检测编码,否则,非UTF-8编码的邮件都无法正常显示:
def guess_charset(msg):
charset = msg.get_charset()
if charset is None:
content_type = msg.get('Content-Type', '').lower()
pos = content_type.find('charset=')
if pos >= 0:
charset = content_type[pos + 8:].strip()
return charset
把上面的代码整理好,我们就可以来试试收取一封邮件。先往自己的邮箱发一封邮件,然后用浏览器登录邮箱,看看邮件收到没,如果收到了,我们就来用Python程序把它收到本地:
Email:17805054367@163.com
Password:**************
POP3 server:pop3.163.com
+OK Welcome to coremail Mail Pop3 Server (163coms[726cd87d72d896a1ac393507346040fas])
*cmd* 'USER 17805054367@163.com'
*cmd* 'PASS ************'
*cmd* 'STAT'
*stat* [b'+OK', b'', b'']
Message: 4. Size: 108715
*cmd* 'LIST'
[b'1 3418', b'2 8115', b'3 94797', b'4 2385']
*cmd* 'RETR 4'
*cmd* 'QUIT'
From: 淮豆居士 <jdouzi@qq.com>
To: 17805054367 <17805054367@163.com>
Subject: 17805054367 <17805054367@163.com>
part 0
-------------------
part 1
-------------------
我们从打印的结构可以看出,这封邮件是一个MIMEMultipart,它包含两部分:第一部分又是一个MIMEMultipart,第二部分是一个附件。而内嵌的MIMEMultipart是一个alternative类型,它包含一个纯文本格式的MIMEText和一个HTML格式的MIMEText。
小结
用Python的poplib模块收取邮件分两步:第一步是用POP3协议把邮件获取到本地,第二步是用email模块把原始邮件解析为Message对象,然后,用适当的形式把邮件内容展示给用户即可。
全部代码:
import poplib # 输入邮件地址, 口令和POP3服务器地址:
from email.parser import Parser # 先导入模块, 用于解析邮件
from email.header import decode_header
from email.utils import parseaddr email = input('Email:')
password = input('Password:')
pop3_server = input('POP3 server:') # 连接到POP3服务器
server = poplib.POP3(pop3_server)
# 可以打开或关闭调试信息:
server.set_debuglevel(1)
# 可选: 打印POP3服务器的欢迎文字:
print(server.getwelcome().decode('utf-8')) # 身份认证:
server.user(email)
server.pass_(password) # stat() 返回邮件数量和占用空间:
print('Message: %s. Size: %s' % server.stat())
# list()返回所有邮件的编号:
resp, mails, octets = server.list()
# 可以查看返回的列表类似[b'1 82923', b'2 2184', ....]
print(mails) # 获取最新一封邮件, 注意索引号从1开始:
index = len(mails)
resp, lines, octets = server.retr(index) # lines存储了邮件的原始文本的每一行,
# 可以获得整个邮件的原始文本:
msg_content = b'\r\n'.join(lines).decode('utf-8')
# 稍后解析出邮件:
msg = Parser().parsestr(msg_content) # 可以根据邮件索引号直接从服务器删除邮件:
# server.dele(index)
# 关闭连接:
server.quit() # 把邮件内容解析为Message对象:
msg = Parser().parsestr(msg_content) # 递归地打印出Message对象的层次结构:
# indent用于缩进显示:
def print_info(msg, indent=0):
if indent == 0:
for header in ['From', 'To', 'Subject']:
value = msg.get(header, '')
if value:
if header == 'Subject':
value = decode_str(value)
else:
hdr, addr = parseaddr(value)
name = decode_str(hdr)
value = u'%s <%s>' % (name, addr)
print('%s%s: %s' % (' ' * indent, header, value))
if (msg.is_multipart()):
parts = msg.get_payload()
for n, part in enumerate(parts):
print('%spart %s' % (' ' * indent, n))
print('%s-------------------' % (' ' * indent))
print_info(part, indent + 1)
else:
content_type = msg.get_content_type()
if content_type=='text/plain' or content_type=='text/html':
content = msg.get_payload(decode=True)
charset = guess_charset(msg)
if charset:
content = content.decode(charset)
print('%sText: %s' % (' ' * indent, content + '...'))
else:
print('%sAttachment: %s' % (' ' * indent, content_type)) def decode_str(s):
value, charset = decode_header(s)[0]
if charset:
value = value.decode(charset)
return value def guess_charset(msg):
charset = msg.get_charset()
if charset is None:
content_type = msg.get('Content-Type', '').lower()
pos = content_type.find('charset=')
if pos >= 0:
charset = content_type[pos + 8 : ].strip()
return charset print_info(msg)
注意:用qq邮箱(修改这段代码)
server = poplib.POP3_SSL(pop3_server,port=995)
Python学习笔记(四十八)POP3收取邮件的更多相关文章
- Python学习笔记第十八周
目录: 一.JavaScript正则表达式 1.test 2.exec 二.BootStrap 1.响应式 2.图标.字体 3.基本使用 三.Django 1.安装 2.创建目录 3.进入 ...
- python学习笔记(十八)网络编程之requests模块
上篇博客中我们使用python自带的urllib模块去请求一个网站,或者接口,但是urllib模块太麻烦了,传参数的话,都得是bytes类型,返回数据也是bytes类型,还得解码,想直接把返回结果拿出 ...
- python学习笔记(十八)python操作excel
python操作excel需要安装通过pip安装xlwt, xlrd这两个模块: pip install xlwt pip insall xlrd 操作excel ,写入excel: import x ...
- Python学习笔记(十八)@property
# 请利用@property给一个Screen对象加上width和height属性, # 以及一个只读属性resolution: # -*- coding: utf-8 -*- class Scree ...
- Python学习笔记(十四)
Python学习笔记(十四): Json and Pickle模块 shelve模块 1. Json and Pickle模块 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不 ...
- Python学习笔记(十)
Python学习笔记(十): 装饰器的应用 列表生成式 生成器 迭代器 模块:time,random 1. 装饰器的应用-登陆练习 login_status = False # 定义登陆状态 def ...
- Java框架spring 学习笔记(十八):事务管理(xml配置文件管理)
在Java框架spring 学习笔记(十八):事务操作中,有一个问题: package cn.service; import cn.dao.OrderDao; public class OrderSe ...
- Python学习笔记(十四):模块高级
以Mark Lutz著的<Python学习手册>为教程,每天花1个小时左右时间学习,争取两周完成. --- 写在前面的话 2013-7-23 21:30 学习笔记 1,包导入是把计算机上的 ...
- Python学习笔记(十五):类基础
以Mark Lutz著的<Python学习手册>为教程,每天花1个小时左右时间学习,争取两周完成. --- 写在前面的话 2013-7-24 23:59 学习笔记 1,Python中的大多 ...
- python学习笔记系列----(八)python常用的标准库
终于学到了python手册的最后一部分:常用标准库.这部分内容主要就是介绍了一些基础的常用的基础库,可以大概了解下,在以后真正使用的时候也能想起来再拿出来用. 8.1 操作系统接口模块:OS OS模块 ...
随机推荐
- 福大软工1816:Alpha事后诸葛
福大软工·第十一次作业-Alpha事后诸葛亮 组长博客链接 本次作业博客链接 项目Postmortem 模板 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描 ...
- PAT 甲级 1023 Have Fun with Numbers
https://pintia.cn/problem-sets/994805342720868352/problems/994805478658260992 Notice that the number ...
- [cnbeta]微软最强数据中心级操作系统
微软近日发表了一篇介绍Windows系统内核的博文,期间为了展示Windows的强大扩展性,放出了一张非常震撼的Windows任务管理器截图:乍一看似乎没啥特别的,几十甚至上百个逻辑核心的系统并不罕见 ...
- C++11 锁 lock
转自:https://www.cnblogs.com/diegodu/p/7099300.html 互斥(Mutex: Mutual Exclusion) 下面的代码中两个线程连续的往int_set中 ...
- excel表中判断A列与B列内容是否相同,相同的话在C列按条件输出!
判断两列数据是否相同,有以下几个函数判断(做笔记于此,方便以后查找): 1.=IF(AND(A4=B4),"相同","") 在C列输出相同字符 2.=IF(A1 ...
- 【bzoj5197】[CERC2017]Gambling Guide 期望dp+堆优化Dijkstra
题目描述 给定一张n个点,m条双向边的无向图. 你要从1号点走到n号点.当你位于x点时,你需要花1元钱,等概率随机地买到与x相邻的一个点的票,只有通过票才能走到其它点. 每当完成一次交易时,你可以选择 ...
- 【Java】自动获取某表某列的最大ID数
使用场景: 当需要往数据库插入数据时,表的主键需要接着已经有的数据后面进行自增.比如已经wq_customer表里,主键为TBL_ID,如果是空表,那么插入的数据TBL_ID设置为1,如果已经有n条数 ...
- BZOJ3573 HNOI2014米特运输
显然确定一个点的权值后整棵树权值确定.只要算出根节点的权值就能知道两种改法是否等价. 乘的话显然会炸,取log即可.map似乎会出一些问题,sort即可. #include<iostream&g ...
- [NOI2011]兔兔与蛋蛋游戏 二分图博弈
题面 题面 题解 通过观察,我们可以发现如下性质: 可以看做是2个人在不断移动空格,只是2个人能移动的边不同 一个位置不会被重复经过 : 根据题目要求,因为是按黑白轮流走,所以不可能重复经过一个点,不 ...
- BZOJ #3746: [POI2015]Czarnoksiężnicy okrągłego stołu 动态规划
转载请注明出处:http://www.cnblogs.com/TSHugh/p/8823423.html 读完题就会发现p=0.1的情况以及n=1.2的情况都可以直接判掉,而p=2的时候也可以直接构造 ...