C語言中文網 目錄

Python poplib模塊:收取郵件

< 上一頁Python smtplib Python文檔和測試下一頁 >

使用 poplib 模塊收取郵件也很簡單,該模塊提供了 poplib.POP3 和 poplib.POP3_SSL 兩個類,分別用于連接普通的 POP 服務器和基于 SSL 的 POP 服務器。

一旦使用 poplib.POP3 或 poplib.POP3_SSL 連接到服務器之后,接下來基本就按照 POP3 協議與服務器進行交互。為了更好地理解 poplib 模塊的運行機制,下面先簡單介紹 POP3 協議內容。

POP3 協議也屬于請求,響應式交互協議,當客戶端連接到服務器之后,客戶端向 POP 服務器發送請求,而 POP 服務器則對客戶端生成響應數據,客戶端可通過響應數據下載得到郵件內容。當下載完成后,郵件客戶端可以刪除或修改任意郵件,而無須與電子郵件服務器進行進一步交互。

POP3 的命令和響應數據都是基于 ASCII 文本的,并以 CR 和 LF(/r/n) 作為行結束符,響應數據包括一個表示返回狀態的符號(+/)和描述信息。

請求和響應的標準格式如下:

請求標準格式:命令 [參數] CRLF
響應標準格式:+OK /[-ERR] description CRLF

POP3 協議客戶端的命令和服務器端對應的響應數據如下:
  • user name:向 POP 服務器發送登錄的用戶名。
  • pass string:向 POP 服務器發送登錄的密碼。
  • quit:退出 POP 服務器。
  • stat:統計郵件服務器狀態,包括郵件數和總大小。
  • list [msg_no]:列出全部郵件或指定郵件。返回郵件編號和對應大小。
  • retr msg_no:獲取指定郵件的內容(根據郵件編號來獲取,編號從 1 開始)。
  • dele msg_no:刪除指定郵件(根據郵件編號來刪除,編號從 1 開始)。
  • noop:空操作。僅用于與服務器保持連接。
  • rset:用于撤銷 dele 命令。

poplib 模塊完全模擬了上面命令,poplib.POP3 或 poplib.POP3_SSL 為上面命令提供了相應的方法,開發者只要依次使用上面命令即可從服務器端下載對應的郵件。

使用 poplib 收取郵件可分為兩步:
  1. 使用 poplib.POP3 或 poplib.POP3_SSL 按 POP3 協議從服務器端下載郵件。
  2. 使用 email.parser.Parser 或 email.parser.BytesParser 解析郵件內容,得到 EmailMessage 對象,從 EmailMessage 對象中讀取郵件內容。

下面程序示范了如何使用 poplib 模塊來收取郵件:
import poplib, os.path, mimetypes
from email.parser import BytesParser, Parser
from email.policy import default

# 輸入郵件地址, 口令和POP3服務器地址:
email = 'kongyeeku@qq.com'
password = '123456'
pop3_server = 'pop.qq.com'

# 連接到POP 3服務器:
#conn = poplib.POP3(pop3_server, 110)
conn = poplib.POP3_SSL(pop3_server, 995)
# 可以打開或關閉調試信息:
conn.set_debuglevel(1)
# 可選:打印POP 3服務器的歡迎文字:
print(conn.getwelcome().decode('utf-8'))
# 輸入用戶名、密碼信息
# 相當于發送POP 3的user命令
conn.user(email)
# 相當于發送POP 3的pass命令
conn.pass_(password)
# 獲取郵件統計信息,相當于發送POP 3的stat命令
message_num, total_size = conn.stat()
print('郵件數: %s. 總大小: %s' % (message_num, total_size))
# 獲取服務器上的郵件列表,相當于發送POP 3的list命令
# resp保存服務器的響應碼
# mails列表保存每封郵件的編號、大小
resp, mails, octets = conn.list()
print(resp, mails)
# 獲取指定郵件的內容(此處傳入總長度,也就是獲取最后一封郵件)
# 相當于發送POP 3的retr命令
# resp保存服務器的響應碼
# data保存該郵件的內容
resp, data, octets  = conn.retr(len(mails))
# 將data的所有數據(原本是一個字節列表)拼接在一起
msg_data = b'\r\n'.join(data)
# 將字符串內容解析成郵件,此處一定要指定policy=default
msg = BytesParser(policy=default).parsebytes(msg_data)       #①
print(type(msg))
print('發件人:' + msg['from'])
print('收件人:' + msg['to'])
print('主題:' + msg['subject'])
print('第一個收件人名字:' + msg['to'].addresses[0].username)
print('第一個發件人名字:' + msg['from'].addresses[0].username)
for part in msg.walk():
    counter = 1
    # 如果maintype是multipart,說明是容器(用于包含正文、附件等)
    if part.get_content_maintype() == 'multipart' :
        continue
    # 如果maintype是multipart,說明是郵件正文部分
    elif part.get_content_maintype() == 'text':
        print(part.get_content())
    # 處理附件
    else :
        # 獲取附件的文件名
        filename = part.get_filename()
        # 如果沒有文件名,程序要負責為附件生成文件名
        if not filename:
            # 根據附件的contnet_type來推測它的后綴名
            ext = mimetypes.guess_extension(part.get_content_type())
            # 如果推測不出后綴名
            if not ext:
                # 使用.bin作為后綴名
                ext = '.bin'
            # 程序為附件來生成文件名
            filename = 'part-%03d%s' % (counter, ext)
        counter += 1
        # 將附件寫入的本地文件
        with open(os.path.join('.', filename), 'wb') as fp:
            fp.write(part.get_payload(decode=True))
# 退出服務器,相當于發送POP 3的quit命令
conn.quit()
上面程序通過 poplib 模塊使用 POP3 命令從服務器端下載郵件,其實就是依次發送 user、pass、stat、list、retr 命令的過程。當 retr 命令執行完成后,將得到最后一封郵件的數據 data,該 data 是一個 list 列表,因此程序需要先將這些數據拼接成一個整體,然后使用 ① 號代碼將郵件數據恢復成 EmailMessage 對象。

這里有一點需要指出,程序在創建 BytesParser(解析字節串格式的郵件數據)或 Parser(解析字符串格式的郵件數據)時,必須指定 policy=default;否則,BytesParse 或 Parser 解析郵件數據得到的就是過時的 Message 對象,處理起來非常不方便。

在創建 BytesParse 或 Parser 解析器時,一定要指定 policy=default;否則,解析出來的對象就是 Message,而不是新的 EmailMessage。

程序在 ① 號代碼之后特意輸出了解析得到的 msg 類型,此時應該看到的是 EmailMesssage,而不是過時的 Message 對象。

在 ① 號代碼之前,就是完成 poplib 模塊收取郵件的第一步,即從服務器端下載郵件;在 ① 號代碼之后,就是完成 poplib 模塊收取郵件的第二步,也就是解析郵件內容。

如果程序要獲取郵件的發件人、收件人和主題,直接通過 EmailMessage 的相應屬性來獲取即可,與前面為 EmailMessage 設置發件人、收件人和主題的方式是對應的。

如果程序要讀取 EmailMessage 的各部分,則需要調用該對象的 walk() 方法,該方法返回一個可迭代對象,程序使用 for 循環遍歷 walk() 方法的返回值,對郵件內容進行逐項處理:
  • 如果郵件某項的 maintype 是 'multipart',則說明這一項是容器,用于包含郵件內容、附件等其他項。
  • 如果郵件某項的 maintype 是 'text',則說明這一項的內容是文本,通常就是郵件正文或文本附件。對于這種文本內容,程序直接將其輸出到控制臺中。
  • 如果郵件某項的 maintype 是其他,則說明這一項的內容是附件,程序將附件內容保存在本地文件中。

運行上面程序,可以看到程序收取了指定郵件的最后一封郵件,并將郵件內容輸出到控制臺中,將郵件附件保存在本地文件中。
< 上一頁Python smtplib Python文檔和測試下一頁 >

精美而實用的網站,提供C語言、C++、STL、Linux、Shell、Java、Go語言等教程,以及socket、GCC、vi、Swing、設計模式、JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo