#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 Parser2 from email.header import decode_header3 from email.utils import parseaddr4 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