21 Python發(fā)送和接收電子郵件

郵件是我們?nèi)粘9ぷ髦兄饕臏贤浇橹?。目前幾乎所有編程語言都支持發(fā)送和接收電子郵件。
接下來將介紹如何使用Python語言發(fā)送和接收郵件。

電子郵件介紹

Email(電子郵件)的歷史比Web還要久遠。直到現(xiàn)在,Email還是互聯(lián)網(wǎng)上應用非常廣泛的服務。
在我們開始編寫郵件操作的相關(guān)代碼之前,先了解一下電子郵件在互聯(lián)網(wǎng)上是如何運作的。
電子郵件其實是我們現(xiàn)實生活中快遞的電子化,現(xiàn)實中快遞是怎么處理的呢?比如你在上海,要郵寄一份文件給北京的朋友。
首先需要準備好郵寄的文件,選擇一家快遞公司(一般是上門取件或到代理點投遞),快遞公司會提供對應的信封,在信封上填寫地址,剩下的事就由快遞公司處理了。
快遞公司會將一個地點的信件從就近的小代理點匯聚到一個快遞中心,再從快遞中心往別的城市發(fā),比如先發(fā)到河南某城市的快遞中心,再從該處發(fā)往北京,也可能由上海直達北京,不過你不用關(guān)心具體路線,只需要知道一件事,就是信件走得比較慢,至少要幾天時間。
信件到達北京的快遞中心后不會直接送到朋友的手里??爝f員為了避免你的朋友不在,而讓自己白跑一趟,會將信件投遞到郵件指定的地址,這個地址可能是你朋友居住附近的快遞箱、家里或所在公司??傊?,當你的朋友知道自己的信件已經(jīng)到達時,就可以取到信件了。
電子郵件的流程基本上是按上面的方式運作的,只不過速度不是按天算,而是按秒算。
現(xiàn)在回到電子郵件,假設自己的電子郵件地址是me@163.com,對方的電子郵件地址是friend@aliyun.com。用Outlook或Foxmail之類的軟件寫好郵件,填上對方的Email地址,單擊“發(fā)送”按鈕,電子郵件就發(fā)送出去了。這些電子郵件被稱為郵件用戶代理(Mail User Agent, MUA)。
Email從MUA發(fā)出去后,不是直接到達對方電腦,而是發(fā)到郵件傳輸代理(Mail Transfer Agent,MTA),就是Email服務提供商,如網(wǎng)易、阿里云等。由于自己的電子郵件地址是163.com,因此Email首先被投遞到網(wǎng)易提供的MTA,再由網(wǎng)易的MTA發(fā)送到對方的服務商,也就是阿里的MTA。在這個過程中可能還會經(jīng)過別的MTA,但是我們不用關(guān)心具體路線,只關(guān)心速度即可。
Email到達阿里的MTA后,由于對方使用的是@aliyun.com的郵箱,因此阿里的MTA會把Email投遞到郵件的最終目的地郵件投遞代理(Mail Delivery Agent, MDA)。Email到達MDA后,會存放在阿里云服務器的某個文件或特殊的數(shù)據(jù)庫里,我們將這個長期保存郵件的地方稱之為電子郵箱。
同普通郵件類似,Email不會直接到達對方的電腦,因為對方的電腦不一定開機,開機也不一定聯(lián)網(wǎng)。對方要取到郵件,必須通過MUA從MDA上獲得。
一封電子郵件的旅程是:
發(fā)件人→MUA→MTA→MTA→若干個MTA→MDA←MUA←收件人
了解了上述基本概念,要編寫程序發(fā)送和接收郵件,本質(zhì)就是:
(1)編寫MUA把郵件發(fā)到MTA。
(2)編寫MUA從MDA上收郵件。
發(fā)郵件時,MUA和MTA使用的協(xié)議是SMTP(Simple Mail Transfer Protocol),后面的MTA到另一個MTA也是用SMTP協(xié)議。
收郵件時,MUA和MDA使用的協(xié)議有兩種:一種是POP(Post Office Protocol),目前版本是3,俗稱POP3;另一種是IMAP(Internet Message Access Protocol),目前版本是4,優(yōu)點是不但能取郵件,而且可以直接操作MDA上存儲的郵件,如從收件箱移到垃圾箱等。
郵件客戶端軟件在發(fā)郵件時,會讓你先配置SMTP服務器,就是要發(fā)到哪個MTA上。假設你正在使用163郵箱,就不能直接發(fā)到阿里的MTA上,因為它只服務于阿里的用戶,所以需要填寫163提供的SMTP服務器地址smtp.163.com。為了證明你是163的用戶,SMTP服務器還要求你填寫郵箱地址和客戶端授權(quán)密碼,這樣MUA才能正常把Email通過SMTP協(xié)議發(fā)送到MTA。
同樣,從MDA收郵件時,MDA服務器也要求驗證你的客戶端授權(quán)密碼,確保不會有人冒充你收取郵件。一般Outlook之類的郵件客戶端會要求填寫POP3或IMAP服務器地址、郵箱地址和授權(quán)密碼。這樣,MUA才能順利通過POP或IMAP協(xié)議,從MDA取到郵件。
在使用Python收發(fā)郵件前,需要先準備好至少兩個電子郵件,如xxx@163.com,xxx@aliyun.com、xxx@qq.com等,注意兩個郵箱不要用同一家郵件服務商。
最后特別注意,目前大多數(shù)郵件服務商都需要手動打開SMTP發(fā)信和POP收信功能,否則只允許在網(wǎng)頁登錄。

發(fā)送郵件

SMTP是發(fā)送郵件的協(xié)議,Python內(nèi)置對SMTP的支持,可以發(fā)送純文本郵件、HTML郵件以及帶附件的郵件。本節(jié)以網(wǎng)易163的服務為例
進行介紹。學習本節(jié)內(nèi)容時,可以自己開通對應的郵箱服務,各個郵件服務公司有介紹郵箱服務的開通方法,參照這些開通方法開通即可。如果已經(jīng)安裝了郵箱服務,就可以使用自己的郵箱服務器進行學習。

1 SMTP發(fā)送郵件

Python對SMTP的支持有smtplib和email兩個模塊,email負責構(gòu)造郵件,smtplib負責發(fā)送郵件。
簡單郵件傳輸協(xié)議(Simple Mail Transfer Protocol, SMTP)是從源地址到目的地址傳送郵件的規(guī)則,由該協(xié)議控制信件的中轉(zhuǎn)方式。
Python的smtplib提供了一種很方便的途徑發(fā)送電子郵件,對SMTP協(xié)議進行了簡單的封裝。
Python創(chuàng)建SMTP對象的語法如下:

smtpObj = smtplib.SMTP([host [, port [, local_hostname]]])

語法中各個參數(shù)說明如下。

  • host: SMTP服務器主機??梢灾付ㄖ鳈C的IP地址或域名(如www.baidu.com),是可選參數(shù)。
  • port:如果提供了host參數(shù),就需要指定SMTP服務使用的端口號。一般情況下SMTP的端口號為25。
  • local_hostname:如果SMTP在本地主機上,只需要指定服務器地址為localhost即可。

如果在創(chuàng)建SMTP對象時提供了host和port兩個參數(shù),在初始化時會自動調(diào)用connect方法連接服務器。
Python SMTP對象使用sendmail方法發(fā)送郵件的語法如下:

SMTP.sendmail(from_addr, to_addrs, msg[, mail_options, rcpt_options]

語法中各個參數(shù)說明如下。

  • from_addr:郵件發(fā)送者的地址。
  • to_addrs:字符串列表,郵件發(fā)送地址。
  • msg:發(fā)送消息。

msg是字符串,表示郵件內(nèi)容。我們知道郵件一般由標題、發(fā)信人、收件人、郵件內(nèi)容、附件等構(gòu)成,發(fā)送郵件時,要注意msg的格式。這個格式就是SMTP協(xié)議中定義的格式。

SMTP類中提供了表1所示的一些常用方法。
1.SMTP類的常用方法

普通文本郵件發(fā)送的實現(xiàn)關(guān)鍵要將MIMEText中的_subtype設置為plain。首先導入smtplib和mimetext。創(chuàng)建smtplib.smtp實例,連接郵件SMTP服務器,登錄后發(fā)送,具體代碼如下:
#! /usr/bin/python
# -*-coding:UTF-8-*-

import smtplib
from email.mime.text import MIMEText
from email.header import Header
sender = 'from@163.com'
pwd = 'xxxxx' #開通郵箱服務后,設置的客戶端授權(quán)密碼
receivers = ['to@aliyun.com'] # 接收郵件,可設置為你的郵箱

# 三個參數(shù):第一個為文本內(nèi)容,第二個 plain 設置文本格式,第三個 utf-8 設置編碼
message = MIMEText('Python 郵件發(fā)送測試...', 'plain', 'utf-8')
message['From'] = Header("郵件測試", 'utf-8')
message['To'] = Header("測試", 'utf-8')

subject = 'Python SMTP 郵件測試'
message['Subject'] = Header(subject, 'utf-8')

try:
    # 使用非本地服務器,需要建立ssl連接
    smtpObj = smtplib.SMTP_SSL("smtp.163.com", 465)
    smtpObj.login(sender, pwd)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print ("郵件發(fā)送成功")
except smtplib.SMTPException as e:
    print ("Error: 無法發(fā)送郵件.Case:%s" % e)

我們使用3個引號設置郵件信息。標準郵件需要3個頭部信息:From、To和Subject。每個信息直接使用空行分割。
我們通過實例化smtplib模塊的SMTP_SSL對象smtpObj連接SMTP訪問,并使用sendmail方法發(fā)送信息。
執(zhí)行以上程序,如果你開通了非本地郵件服務,就會輸出:

郵件發(fā)送成功

如果本地主機安裝了sendmail服務,發(fā)送郵件的代碼可以更改為:

sender = 'from@163.com'
receivers = ['to@aliyun.com'] # 接收郵件,可設置為你的郵箱

# 三個參數(shù):第一個為文本內(nèi)容,第二個 plain 設置文本格式,第三個 utf-8 設置編碼
message = MIMEText('Python 郵件發(fā)送測試...', 'plain', 'utf-8')
message['From'] = Header("郵件測試", 'utf-8')
message['To'] = Header("測試", 'utf-8')

subject = 'Python SMTP 郵件測試'
message['Subject'] = Header(subject, 'utf-8')

try:
    smtpObj = smtplib.SMTP("localhost")
    smtpObj.sendmail(sender, receivers, message.as_string())
    print ("郵件發(fā)送成功")
except smtplib.SMTPException as e:
    print ("Error: 無法發(fā)送郵件.Case:%s" % e)

不需要客戶端授權(quán)密碼、SSL連接和登錄服務。

根據(jù)示例替換相應的授權(quán)密碼、SSL連接和登錄服務后,本地執(zhí)行效果如圖2所示。
1 普通郵件發(fā)送效果

2 發(fā)送HTML格式的郵件

如果我們要發(fā)送的是HTML郵件,而不是普通的純文本文件怎么辦呢?方法很簡單,在構(gòu)造MIMEText對象時把HTML字符串傳進去,再把第二個參數(shù)由plain變?yōu)閔tml就可以了。代碼實現(xiàn)如下:

#! /usr/bin/python
# -*-coding:UTF-8-*-

import smtplib
from email.mime.text import MIMEText
from email.header import Header

sender = 'from@163.com'
pwd = 'xxxxx' #開通郵箱服務后,設置的客戶端授權(quán)密碼
receivers = ['to@aliyun.com'] # 接收郵件,可設置為你的郵箱

mail_msg = """
<p>Python 郵件發(fā)送測試...</p>
<p><a >這是一個鏈接</a></p>
"""
message = MIMEText(mail_msg, 'html', 'utf-8')
message['From'] = Header("郵件測試", 'utf-8')
message['To'] = Header("測試", 'utf-8')
subject = 'Python SMTP 郵件測試'
message['Subject'] = Header(subject, 'utf-8')

try:
    # 使用非本地服務器,需要建立ssl連接
    smtpObj = smtplib.SMTP_SSL("smtp.163.com", 465)
    smtpObj.login(sender, pwd)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print ("郵件發(fā)送成功")
except smtplib.SMTPException as e:
    print ("Error: 無法發(fā)送郵件.Case:%s" % e)

執(zhí)行以上程序,如果你開通了非本地郵件服務,就會輸出:

郵件發(fā)送成功

如果本地主機安裝了sendmail服務,就不需要客戶端授權(quán)密碼、SSL連接和登錄服務,直接使用smtplib模塊的SMTP對象連接本地訪問即可。

3 發(fā)送帶附件的郵件

如果Email中要添加附件怎么辦?
帶附件的郵件可以看作包含文本和各個附件,可以構(gòu)造一個MIMEMultipart對象代表郵件本身,然后往里面添加一個MIMEText作為郵件正文,再添加表示附件的MIMEBase對象即可。代碼實現(xiàn)如下:

#! /usr/bin/python
# -*-coding:UTF-8-*-

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header

sender = 'from@163.com'
pwd = 'xxxxx' #開通郵箱服務后,設置的客戶端授權(quán)密碼
receivers = ['to@aliyun.com'] # 接收郵件,可設置為你的郵箱

#創(chuàng)建一個帶附件的實例
message = MIMEMultipart()
message['From'] = Header("郵件測試", 'utf-8')
message['To'] = Header("測試", 'utf-8')
subject = 'Python SMTP 郵件測試'
message['Subject'] = Header(subject, 'utf-8')

#郵件正文內(nèi)容
message.attach(MIMEText('這是Python 郵件發(fā)送測試……', 'plain', 'utf-8'))

# 構(gòu)造附件1,傳送當前目錄下的 test.txt 文件
att1 = MIMEText(open('test.txt', 'rb').read(), 'base64', 'utf-8')
att1["Content-Type"] = 'application/octet-stream'
# 這里的filename 可以任意寫,寫什么名字,郵件中就顯示什么名字
att1["Content-Disposition"] = 'attachment; filename="test.txt"'
message.attach(att1)

# 構(gòu)造附件2,傳送當前目錄下的 runoob.txt 文件
att2 = MIMEText(open('runoob.txt', 'rb').read(), 'base64', 'utf-8')
att2["Content-Type"] = 'application/octet-stream'
att2["Content-Disposition"] = 'attachment; filename="runoob.txt"'
message.attach(att2)

try:
    # 使用非本地服務器,需要建立ssl連接
    smtpObj = smtplib.SMTP_SSL("smtp.163.com", 465)
    smtpObj.login(sender, pwd)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print ("郵件發(fā)送成功")
except smtplib.SMTPException as e:
    print ("Error: 無法發(fā)送郵件.Case:%s" % e)

執(zhí)行以上程序,如果你開通了非本地郵件服務,就會輸出:
郵件發(fā)送成功
如果本地主機安裝了sendmail服務,就不需要客戶端授權(quán)密碼、SSL連接和登錄服務,直接使用smtplib模塊的SMTP對象連接本地訪問即可。

4 發(fā)送圖片

如果要把一個圖片嵌入郵件正文,怎么做呢?是否可以直接在HTML郵件中鏈接圖片地址?
大部分郵件服務商都會自動屏蔽帶有外鏈的圖片,因為不知道這些鏈接是否指向惡意網(wǎng)站。要把圖片嵌入郵件正文,我們只需按照發(fā)送附件的方式把郵件作為附件添加進去,然后在HTML中通過引用src="cid:0"把附件作為圖片嵌入。如果有多張圖片,就需要給它們依次編號,然后引用不同的cid:x。

#! /usr/bin/python
# -*-coding:UTF-8-*-

import smtplib
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header

sender = 'from@163.com'
pwd = 'xxxxx' #開通郵箱服務后,設置的客戶端授權(quán)密碼
receivers = ['to@aliyun.com'] # 接收郵件,可設置為你的郵箱

msgRoot = MIMEMultipart('related')
msgRoot['From'] = Header("郵件測試", 'utf-8')
msgRoot['To'] =  Header("測試", 'utf-8')
subject = 'Python SMTP 郵件測試'
msgRoot['Subject'] = Header(subject, 'utf-8')

msgAlternative = MIMEMultipart('alternative')
msgRoot.attach(msgAlternative)
mail_msg = """
<p>Python 郵件發(fā)送測試...</p>
<p><a >Python 官方網(wǎng)站</a></p>
<p>圖片演示:</p>
<p><img src="cid:image1"></p>
"""
msgAlternative.attach(MIMEText(mail_msg, 'html', 'utf-8'))

# 指定圖片為當前目錄
fp = open('test.png', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()

# 定義圖片 ID,在 HTML 文本中引用
msgImage.add_header('Content-ID', '<image1>')
msgRoot.attach(msgImage)

try:
    # 使用非本地服務器,需要建立ssl連接
    smtpObj = smtplib.SMTP_SSL("smtp.163.com", 465)
    smtpObj.login(sender, pwd)
    smtpObj.sendmail(sender, receivers, msgRoot.as_string())
    print ("郵件發(fā)送成功")
except smtplib.SMTPException as e:
    print ("Error: 無法發(fā)送郵件.Case:%s" % e)

執(zhí)行以上程序,如果你開通了非本地郵件服務,就會輸出:

郵件發(fā)送成功

如果本地主機安裝了sendmail服務,就不需要客戶端授權(quán)密碼、SSL連接和登錄服務,直接使用smtplib模塊的SMTP對象連接本地訪問即可。

5 同時支持HTML和Plain格式

如果我們發(fā)送HTML郵件,收件人通過瀏覽器或Outlook之類的軟件就可以正常瀏覽郵件內(nèi)容。如果收件人使用的設備太古老,查看不了HTML郵件怎么辦呢?
辦法是在發(fā)送HTML的同時附加一個純文本,如果收件人無法查看HTML格式的郵件,就可以自動降級查看純文本郵件。
利用MIMEMultipart可以組合一個HTML和Plain,注意指定subtype是alternative。使用代碼格式如下:

#! /usr/bin/python
# -*-coding:UTF-8-*-

import smtplib
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header

sender = '15618942985@163.com'
pwd = 'lyz111LYZ' #開通郵箱服務后,設置的客戶端授權(quán)密碼
receivers = ['yuzhouliu@aliyun.com'] # 接收郵件,可設置為你的郵箱

msgRoot = MIMEMultipart('related')
msgRoot['From'] = Header("郵件測試", 'utf-8')
msgRoot['To'] =  Header("測試", 'utf-8')
subject = 'Python SMTP 郵件測試'
msgRoot['Subject'] = Header(subject, 'utf-8')

msgAlternative = MIMEMultipart('alternative')
msgRoot.attach(msgAlternative)

msgAlternative.attach(MIMEText('hello', 'plain', 'utf-8'))
mail_msg = '<html><body><h1>Hello</h1></body></html>'
msgAlternative.attach(MIMEText(mail_msg, 'html', 'utf-8'))

# 指定圖片為當前目錄
fp = open('test.png', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()

# 定義圖片 ID,在 HTML 文本中引用
msgImage.add_header('Content-ID', '<image1>')
msgRoot.attach(msgImage)

try:
    # 使用非本地服務器,需要建立ssl連接
    smtpObj = smtplib.SMTP_SSL("smtp.163.com", 465)
    smtpObj.login(sender, pwd)
    smtpObj.sendmail(sender, receivers, msgRoot.as_string())
    print ("郵件發(fā)送成功")
except smtplib.SMTPException as e:
    print ("Error: 無法發(fā)送郵件.Case:%s" % e)

執(zhí)行以上程序,如果你開通了非本地郵件服務,就會輸出:

郵件發(fā)送成功

查看收到的郵件,如圖2所示。


2 接收帶附件的郵件

6 加密SMTP

使用標準25端口連接SMTP服務器時使用的是明文傳輸,發(fā)送郵件的整個過程可能會被竊聽。要更安全地發(fā)送郵件,可以加密SMTP會話,實際上是先創(chuàng)建SSL安全連接,然后使用SMTP協(xié)議發(fā)送郵件。
某些郵件服務商(如Gmail)提供的SMTP服務必須進行加密傳輸。下面來看如何通過Gmail提供的安全SMTP發(fā)送郵件。
由于Gmail的SMTP端口是587,因此修改代碼如下:

smtp_server = 'smtp.gmail.com'
smtp_port = 587
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
# 剩下的代碼和前面的一模一樣:
server.set_debuglevel(1)
...

只需要在創(chuàng)建SMTP對象后立刻調(diào)用starttls()方法,就可以創(chuàng)建安全連接。后面的代碼和前面的發(fā)送郵件代碼完全一樣。
如果因為網(wǎng)絡問題無法連接Gmail的SMTP服務器,請相信我們的代碼是沒有問題的,需要對網(wǎng)絡設置做必要的調(diào)整。

POP3接收郵件

SMTP用于發(fā)送郵件,如果要收取郵件呢?
收取郵件就是編寫一個MUA作為客戶端,從MDA獲取郵件到用戶的電腦或手機上。收取郵件最常用的協(xié)議是POP,目前版本是3,俗稱POP3。
Python內(nèi)置了一個poplib模塊,用于實現(xiàn)POP3協(xié)議,可以直接用來收取郵件。
注意POP3協(xié)議收取的不是可以閱讀的郵件,而是郵件的原始文本。這和SMTP協(xié)議很像,SMTP發(fā)送的也是經(jīng)過編碼后的一大段文本。
要把POP3收取的文本變成可以閱讀的郵件,還需要用email模塊提供的各種類解析原始文本。
收取郵件分為以下兩個步驟:
(1)用poplib把郵件的原始文本下載到本地。
(2)用email解析原始文本,還原為郵件對象。

1 POP3下載郵件

POP3協(xié)議很簡單。下面獲取最新一封郵件的內(nèi)容,代碼如下:

#! /usr/bin/python
# -*-coding:UTF-8-*-

import poplib
from email.parser import Parser

# 輸入郵件地址、口令和POP3服務器地址
email = input('Email: ')
password = input('Password: ')
pop3_server = input('POP3 server: ')

# 連接到POP3 服務器
server = poplib.POP3(pop3_server)
# 可以打開或關(guān)閉調(diào)試信息
server.set_debuglevel(1)
# 可選:輸出POP3服務器的歡迎文字
print(server.getwelcome().decode('utf-8'))

# 身份認證
server.user(email)
server.pass_(password)
# stat()返回郵件數(shù)量和占用空間
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'\r\n'.join(lines).decode('utf-8')
# 稍后解析郵件
msg = Parser().parsestr(msg_content)

# 可以根據(jù)郵件索引號直接從服務器刪除郵件
# server.dele(index)
# 關(guān)閉連接
server.quit()

用POP3獲取郵件其實很簡單,要獲取所有郵件,只需要循環(huán)使用retr()把每一封郵件的內(nèi)容拿到即可。真正麻煩的是把郵件的原始內(nèi)容解析為可以閱讀的郵件對象。

2 解析郵件

解析郵件的過程和構(gòu)造郵件正好相反,需要先導入必要的模塊:

from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr

import poplib

只需要一行代碼就可以把郵件內(nèi)容解析為Message對象:

msg = Parser().parsestr(msg_content)

這個Message對象可能是一個MIMEMultipart對象,即包含嵌套的其他MIMEBase對象,嵌套可能還不止一層。
我們要遞歸地輸出Message對象的層次結(jié)構(gòu):

# 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('%spart %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('%sText: %s' % (' ' * indent, content + '...'))
         else:
             print('%sAttachment: %s' % (' ' * indent, content_type))

郵件的Subject或Email中包含的名字都是經(jīng)過編碼的str,要正常顯示必須進行解碼,代碼如下:

def decode_str(s):
    value, charset = decode_header(s)[0]
    if charset:
         value = value.decode(charset)
    return value

decode_header()返回一個list,因為像Cc、Bcc這樣的字段可能包含多個郵件地址,所以會解析出多個元素。編寫上面的代碼時偷懶了,只取了第一個元素。
文本郵件的內(nèi)容也是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 pos >= 0:
             charset = content_type[pos + 8:].strip()
    return charset
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容