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模块 ...
随机推荐
- python knn自我实践
#得到分类数据和测试数据 import pymysql import struct from numpy import * a=['']*20 #存图像 分类数据 b=[[0]*76800]*20#存 ...
- Java 线程安全问题
线程安全问题产生原因: 1.多个线程操作共享的数据: 2.操作共享数据的线程代码有多条. 当一个线程正在执行操作共享数据的多条代码过程中,其它线程也参与了运算, 就会导致线程安全问题的发生. cl ...
- 测试——约跑APP
项目名:约跑APP 用户需求规格说明书URL:http://www.cnblogs.com/liquan/p/6071804.html 组长博客URL:http://www.cnblogs.com/l ...
- ZOJ 2060 A-Fibonacci Again
https://vjudge.net/contest/67836#problem/A There are another kind of Fibonacci numbers: F(0) = 7, F( ...
- linux下c/c++的文件操作
opendir,readdir,closedir, stat()查询文件状态 open(), O_TRUNC这个Flag会把打开的文件清零... 文件锁:fcntl, F_GETLK , F_SETL ...
- [C/C++] 虚函数机制
转自:c++ 虚函数的实现机制:笔记 1.c++实现多态的方法 其实很多人都知道,虚函数在c++中的实现机制就是用虚表和虚指针,但是具体是怎样的呢?从more effecive c++其中一篇文章里面 ...
- 网页移动到一个高度后加载网页元素【getBoundingClientRect好用】
$(window).scroll(function () { var windowH = $(window).height();//取可视窗口的高度 ).getBoundingClientRect() ...
- 【.Net】在WinForm中选择本地文件
相信很多朋友在日常的编程中总会遇到各钟各样的问题,关于在WinForm中选择本地文件就是很多朋友们都认为很难的一个学习.net的难点, 在WebForm中提供了FileUpload控件来供我们选择本地 ...
- tarjan强连通分量模板(pascal)
友好城市 [问题描述]小 w 生活在美丽的 Z 国. Z 国是一个有 n 个城市的大国, 城市之间有 m 条单向公路(连接城市 i. j 的公路只能从 i 连到 j). 城市 i. j 是友好城市当且 ...
- NoSQL - Redis应用场景
问题的引入 DB(Oracle.MySQL.Postgresql等)+Memcached 这种架构模式在我们生产环境中十分常见,一般我们通过Memcached将热点数据加载到cache,应用 ...