#SMTP用于发送邮件,如果要收取邮件呢?
#收取邮件就是编写一个MUA作为客户端,从MDA获取邮件到用户的电脑或手机上。收取邮件最常用的协议是POP,目前版本是3,俗称POP3.
#Python内置了一个poplib模块,用于实现POP3协议,可以直接用来收取邮件。
#注意POP3协议收取的不是可以阅读的邮件,而是邮件的原始文本。这和SMTP协议很像,SMTP发送的也是经过编码后的一大段文本。
#要把POP3收取的文本变成可以阅读的邮件,还需要用email模块提供的各种类解析原始文本。
#收取邮件分为以下两个步骤
1)、用poplib把邮件的原始文本下载到本地。
2)、用email解析原始文本,还原为邮件对象。
1、POP下载邮件
#pop3协议很简单。下面获取最新一封邮件的内容,代码如下:
1 #! /usr/bin/python3 2 #-*-coding:UTF-8-*- 3 #Email_pop3 4 5 import poplib 6 from email.parser import Parser 7 8 #输入邮箱地址、密码和POP服务器地址 9 email=input(‘Email:‘) 10 password=input(‘Password:‘) 11 pop3_server=input(‘POP3 server:‘) 12 13 #连接到POP3服务器 14 server=poplib.POP3(pop3_server) 15 16 #可以打开或关闭调试信息 17 server.set_debuglevel(1) 18 19 #可选:输出pop3服务器的欢迎文字 20 print(server.getwelcome().decode(‘utf-8‘)) 21 22 #身份认证 23 server.user(email) 24 server.pass_(password) 25 26 #stat()返回邮件数量和占用空间 27 print(‘Message:%s.Size:%s‘%server.stat()) 28 29 #list()返回所有邮件的编号 30 resp,mails,octets=server.list() 31 32 #可以查看返回的列表,类似[b‘1 82923‘,b‘2 2184‘,...] 33 print(mails) 34 35 #获取最新一封邮件,注意索引号从1开始 36 index=len(mails) 37 resp,lines,octets=server.retr(index) 38 39 #lines存储了邮件原始文本的每一行 40 #可以获得整个邮件的原始文本 41 msg_content=b‘\r\n‘.join(lines).decode(‘utf-8‘) 42 43 #稍后解析邮件 44 msg=Parser().parsestr(msg_content) 45 46 #可以根据邮件索引号直接从服务器删除邮件 47 #server.dele(index) 48 #关闭连接 49 server.quit()
#用POP3获取邮件其实很简单,要获取所有邮件,只需要循环使用retr()把每一封邮件的内容拿到即可。真正麻烦的是把邮件的原始内容解析为可以阅读的邮件对象。
2、解析邮件
#解析邮件的过程和构造邮件正好相反,需要先导入必要的模块:
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 def print_info(msg,indent=0): 2 if indent==0: 3 for header in [‘From‘,‘To‘,‘Subject‘]: 4 value=msg.get(header,‘‘) 5 if value: 6 if header==‘Subject‘: 7 value=decode_str(value) 8 else: 9 hdr,addr=parseaddr(value) 10 name=decode_str(hdr) 11 value=u‘%s<%s>‘%(name,addr) 12 print(‘%s%s:%s‘%(‘‘*indent,header,value)) 13 if(msg,is_multipart()): 14 parts=msg.get_payload() 15 for n,part in enumerate(parts): 16 print(‘%spart %s‘%(‘‘*indent,n)) 17 print(‘%s------------‘%(‘‘*indent)) 18 print_info(part,indent+1) 19 else: 20 content_type=msg.get_content_type() 21 if content_type==‘text/plain‘ or content_type==‘text/html‘: 22 content=msg.get_payload(decode=True) 23 charset=guess_charset(msg) 24 if charset: 25 content=content.decode(charset) 26 print(‘%sText:%s‘%(‘‘*indent,content+‘...‘)) 27 else: 28 print(‘%sAttachment:%s‘%(‘‘*indent,content_type))
#邮件的Subject或Email中包含的名字都是经过编码的str,要正常显示必须进行解码,代码如下:
1 def decode_str(s): 2 value,charset=decode_header(s)[0] 3 if charset: 4 value=value.decode(charset) 5 return value
#decode_header()返回一个list,因为像Cc、Bcc这样的字段可能包含多个邮件地址,所以会解析出多个元素。
#文本邮件的内容也是str,好需要检测编码,否则非UTF-8编码的邮件都无法正常显示,代码如下:
1 def guess_charset(msg): 2 charset=msg.get_charset() 3 if charset is None: 4 content_type=msg.get(‘Content-Type‘,‘‘).lower() 5 pos=content_type.find(‘charset=‘) 6 if pos >=0: 7 chatset=content_type[pos+8:].strip() 8 return charset