Python 15.2 POP3 收取郵
- 2020 年 1 月 6 日
- 筆記
POP3收取郵件
收取郵件就是編寫一個MUA作為客戶端,從MDA把郵件獲取到用戶的電腦或者手機上。收取郵件最常用的就是POP3協議。
Python內置了一個poplib模塊,實現了POP3協議,可以直接用來收取郵件。
注意,POP3收取不是一個可以直接閱讀的郵件本身,而是郵件的原始文本,這和SMTP很像,SMTP也是發送一大堆經過編碼的文本。要把POP3收取的文本變成可閱讀的郵件,還需要用email模塊提供的各種類來解析原始文本,變成可閱讀的對象。
所以,收取郵件分為兩部分: 1、用poplib把郵件的原始文本下載到本地。
2、用email解析原始文本,還原為郵件對象。
通過POP3下載郵件
POP3協議本身很簡單,以下面代碼為例,我們來獲取最新的一封郵件內容: import poplib
email =imput('Email:')
password =input('Password:')
pop3_server =input('POP3 Server:')
server =poplib.POP3(pop3_server)
server.set_debuglevel(1)
print(server.getwelcome().decode('utf-8'))
#身份認證: server.user(email)
server.pass_(password)
#stat返回郵件的數量和佔用的空間: 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存儲了原始文本郵件的每一行
#可以獲得整個原始文本的內容
msg_content =b'rn',join(lines).decode('utf-8')
#稍後解析出郵件
msg =Parser().parsestr(msg_content)
#可以根據索引直接從服務器上刪除郵件: server.dele(index)
#關閉連接: server.close()
用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對象本身可能是一個MessageMultipart對象,即包含嵌套的其他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('%s part %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('%s Text: %s' % (' ' * indent,content + '…'))
else:
print('%s Attachment: %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 poss >= 0:
charset =content_type[pos + 8:].strip()
return charset
把上面的代碼整理好,我們就可以收取一封郵件了。
+OK Welcome to coremail Mail Pop3 Server (163coms[...]) Messages: 126. Size: 27228317 From: Test <[email protected]>To: Python愛好者 <[email protected]>Subject: 用POP3收取郵件 part 0 -------------------- part 0 -------------------- Text: Python可以使用POP3收取郵件……... part 1 -------------------- Text: Python可以<a href="...">使用POP3</a>收取郵件……... part 1 -------------------- Attachment: application/octet-stream
我們從大陰中可以看出,這封郵件是一個MIMEMultiport,它包含兩部分,第一部分又是一個MIMEMultiport,第二部分是個附件。而內嵌的MIMEMultipart和一個HTML格式的MIMEText。
小結: 用poplib模塊收取郵件分兩步:第一部分使用POP3協議將郵件獲取到本地,第二步使用email模塊把原始郵件解析為Message對象,然後用適當的形式顯示給用戶即可。