Python学习之旅(三十六)
Python基础知识(35):电子邮件(Ⅱ)
收取邮件就是编写一个MUA作为客户端,从MDA把邮件获取到用户的电脑或者手机上
收取邮件最常用的协议是POP协议,目前版本号是3,俗称POP3
Python内置一个poplib
模块,实现了POP3协议,可以直接用来收邮件
1、通过POP3协议下载邮件
获取最新的一封邮件内容
import poplib from email.parser import Parser #输入邮件地址,口令和POP3服务器地址 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) #start()返回邮件数量和占用空间 print('Messages: %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存储了邮件的1原始文本的每一行 #可以获得整个邮件的原始文本 msg_content = b'\r\n'.join(lines).decode('utf-8') #稍后解析出邮件 msg = Parser().parsestr(msg_content) #可以根据邮件索引号直接从服务器删除邮件 #server.dele(index) #关闭连接 server.close()
要获取所有邮件,只需要循环使用retr()
把每一封邮件内容拿到即可。真正麻烦的是把邮件的原始内容解析为可以阅读的邮件对象
运行结果如下
Email: xxx@163.com Password: xxxxx(这里填的是授权码) POP3 server: pop.163.com +OK Welcome to coremail Mail Pop3 Server (163coms[b62aaa251425b4be4eaec4ab4744cf47s]) *cmd* 'USER xxx@163.com' *cmd* 'PASS xxxxx' *cmd* 'STAT' *stat* [b'+OK', b'1', b'2375'] Messages: 1. Size: 2375 *cmd* 'LIST' [b'1 2375'] *cmd* 'RETR 1'
2、解析邮件
解析邮件的过程和SMTP协议构造邮件正好相反,因此,先导入必要的模块
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
对象的层次结构
#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('%sAttachemnt: %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
文本邮件的内容也是str,还需要检测编码,否则,非UTF-8编码的邮件都无法正常显示
def guess_charset(msg): charset = msg.get_charset() if charset is None: content_type = msg.get('Cnotne-Type', '').lower() pos = content_type.find('charset=') if pos >= 0: charset = content_type[pos + 8:].strip() return charset
完整代码如下:
from email.parser import Parser from email.header import decode_header from email.utils import parseaddr import poplib #输入邮件地址,口令和POP3服务器地址 email = input('Email: ') password = input('Password: ') pop3_server = input('POP3 server: ') 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 # 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)) #连接到POP3服务器 server = poplib.POP3(pop3_server) #可以打开或关闭调试信息 server.set_debuglevel(1) #可选:打印POP3服务器的欢迎文字 print(server.getwelcome().decode('utf-8')) #身份认证 server.user(email) server.pass_(password) #start()返回邮件数量和占用空间 print('Messages: %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存储了邮件的1原始文本的每一行 #可以获得整个邮件的原始文本 msg_content = b'\r\n'.join(lines).decode('utf-8') #稍后解析出邮件 msg = Parser().parsestr(msg_content) print_info(msg) #关闭连接 server.close()
运行结果如下:
Email: xxx@163.com Password: xxxxx POP3 server: pop.163.com +OK Welcome to coremail Mail Pop3 Server (163coms[b62aaa251425b4be4eaec4ab4744cf47s]) *cmd* 'USER xxx@163.com' *cmd* 'PASS xxxxx' *cmd* 'STAT' *stat* [b'+OK', b'1', b'2375'] Messages: 1. Size: 2375 *cmd* 'LIST' [b'1 2375'] *cmd* 'RETR 1' From: xxxx<xxxx@qq.com> To: xxx <xxx@163.com> Subject: Hello, world! part 0 -------------------- Text: ... part 1 -------------------- Text: <div><br></div><div> </div>...
资料来源:
1、廖雪峰学习官网
2、kornberg_fresnel的博文:https://blog.csdn.net/kornberg_fresnel/article/details/51227761