一、簡介
POP(Post Office Protocal)最長用的POP版本是POP3,因此本文是以POP3為主。POP3非常簡單,可以用來從郵件服務器上下載郵件,然后刪除這些郵件。功能非常有限,后面講解的IMAP完勝它,不過作為入門級的,還是有必要介紹一下,也對學習SMTP有幫助。
Python提供了poplib模塊,它提供了使用POP的便利接口。
二、實例
由於pop3功能較IMAP非常有限,而且我最后的程序並沒有使用pop3,所以,不詳細講解,下面通過一個例子來說明下較為常見的功能。
這個例子的功能為進入郵箱,查看所有的郵件。首先顯示郵件的發件人、主題,查看郵箱主題內容。
1. 需要模塊
import email, poplib, sys
2. 連接POP3服務器,登錄個人郵箱賬戶
poplib提供POP3()方法和POP3_SSL()方法連接POP3服務器,區別和SMTP一樣。gmail仍然使用POP3_SSL()方式,並返回class POP3實例
p = poplib.POP3_SSL('pop.gmail.com')
使用POP3.user(), POP3.pass_()方法來登錄個人賬戶
try: p.user(user) p.pass_(passwd) except poplib.error_proto: #可能出現的異常 print('login failed')
3. 現在已經進入個人賬戶,下一步,利用POP3.list()函數查看郵箱內郵件信息。
關於list()函數的詳細說明,請點擊這里。
list()函數有三個返回值,分別是:response, listings, octets
- response 應答信息,我測試中出現的結果:

以b開頭的字符串是Byte類型,我在實際測試的時候,返回的信息幾乎都是Byte類型的。關於此類型及和普通字符串的轉化會在后面舉例說明。
- listings 是形如['message_id message_size',...]若干各message-id和message_size構成的list。后面就是通過message_id來檢索郵件。我測試中出現的結果:
- octets 不是特別清楚啥意思。
response, listings, octets = p.list()
4. 最重要的就是listings數據
如上面解釋的,listings是個list類型的數據,接下來我們取出listings中的message_id,也就是上面的 "1" "2" "3" "4" ...
for listing in listings: #每次需要一個listing number, size = listing.split() #由於number和size是以空格分隔,所以利用split()函數分開,split()默認以' '為分隔
現在我們就取出了我們需要的message_id,也就是number,注意number需要從Byte類型轉化為字符串類型。
5. POP3.top()函數
利用此函數,取出郵件的headers,如下:
response, lines, octets = p.top(number , 0)
lines存儲內容,下面先轉化成Message類型(lines默認為標准字符串類型,僅供說明,以實際代碼為准)
message = email.message_from_string('\n'.join(lines))
6. 已經生成Message類,可以利用頭部信息來查看From, Subject等信息
for header in 'From', 'To', 'Subject', 'Date': if header in message: print(header + ':' , message[header])
注意,此時的message[header]可能不會輸出我們想看到的內容,有可能出現格式錯亂問題,比如中英文的轉化,所以還需要特殊來處理。處理方式請繼續往下看IMAP部分。
7. 取出郵件所有信息
上面的top()函數只取出header信息以及根據參數確定的n行內容,如果用戶希望查看郵件所有內容,那利用POP3.retr()函數取出
response, lines, octets = p.retr(number)
還是將lines中的內容轉換成Message類型:
message = email.message_from_string('\n'.join(lines))
8. 已經有了郵件所有信息,可以通過Message.get_payload()取出郵件正文了。
但是,get_payload()函數並不一定返回郵件正文。以下是官方說明:
Return the current payload, which will be a list of Message objects when is_multipart() is True, or a string when is_multipart() is False.
在實際測試中,返回的就是a list of Message objects,這個問題困擾我很長時間,最終還是解決了,通過以下方法:
maintype = message.get_content_maintype() if maintype == 'multipart': for part in message.get_payload(): if part.get_content_maintype() == 'text': mail_content = part.get_payload(decode=True).strip() elif maintype == 'text': mail_content = e.get_payload(decode=True).strip()
9. 此時,mail_content就是郵件正文了.
當然,如果是中文的話,這件事仍未完,還需要將它轉化未'gbk',利用如下方式:
mail_content = mail_content.decode('gbk')
10. 到現在,基本已經大功告成了,能夠取出郵箱中所有的郵件,並查看郵件的header內容和郵件正文了^_^
三、完整代碼:
#-*- encoding:utf-8 -*- #-*- encoding:gbk -*- import email, getpass, poplib, sys hostname = 'pop.gmail.com' user = 'myUserName@gmail.com' passwd = '***' p = poplib.POP3_SSL('pop.gmail.com') #與SMTP一樣,登錄gmail需要使用POP3_SSL() 方法,返回class POP3實例 try: # 使用POP3.user(), POP3.pass_()方法來登錄個人賬戶 p.user(user) p.pass_(passwd) except poplib.error_proto: #可能出現的異常 print('login failed') else: response, listings, octets = p.list() for listing in listings: number, size = listing.split() #取出message-id number = bytes.decode(number) size = bytes.decode(size) print('Message', number, '( size is ', size, 'bytes)') print() response, lines, octets = p.top(number , 0) # 繼續把Byte類型轉化成普通字符串 for i in range(0, len(lines)): lines[i] = bytes.decode(lines[i]) #利用email庫函數轉化成Message類型郵件 message = email.message_from_string('\n'.join(lines)) # 輸出From, To, Subject, Date頭部及其信息 for header in 'From', 'To', 'Subject', 'Date': if header in message: print(header + ':' , message[header]) #與用戶交互是否想查看郵件內容 print('Read this message [ny]') answer = input() if answer.lower().startswith('y'): response, lines, octets = p.retr(number) #檢索message並返回 for i in range(0, len(lines)): lines[i] = bytes.decode(lines[i]) message = email.message_from_string('\n'.join(lines)) print('-' * 72) maintype = message.get_content_maintype() if maintype == 'multipart': for part in message.get_payload(): if part.get_content_maintype() == 'text': mail_content = part.get_payload(decode=True).strip() elif maintype == 'text': mail_content = e.get_payload(decode=True).strip() try: mail_content = mail_content.decode('gbk') except UnicodeDecodeError: print('Decoding to gbk error') sys.exit(1) print(mail_content) print() print('Delete this message? [ny]') answer = input() if answer.lower().startswith('y'): p.dele(number) print('Deleted') finally: print('log out') p.quit()