郵件的解析是個大課題,遠超一般人的預期。它遠比發送郵件和接收郵件要複雜的多的多。
這就是爲什麼網上中文外文搜郵件的問題,絕大多數都是講發送的而講接收的很少。
發送郵件好說,接收和下載郵件也好說。關鍵是下載下來的郵件是一種比HTML還複雜的嵌套結構
MIME郵件協議
參考:阮一峯 - MIME筆記
參考:MIME郵件的組織結構
參考:Parsing email using Python part 2 of 2 : The content
參考:Mozilla - MIME types
先不論Python,也不談什麼郵件發展歷史,只論現在:
現在我們要達到通過編程來解析郵件,就絕對避不開這個問題:MIME郵件結構
.
MIME是一整套的協議,就像HTTP協議、TCP協議之類的一樣,都是解析郵件的一套規則。
所以我們想要解析一封郵件(把它拆成人能讀懂的標題、收發件人、內容、附件等),就必須得理解這套協議。
就算有現成的Python處理庫也一樣要懂了以後才能開始操作。
瞭解MIME協議,其實主要就是了解郵件的嵌套結構
。這個懂了就全懂了。
要知道,我們收到的一封郵件可能是以下這幾種不同的結構類型:
- 簡單的幾句話,全是文字。
(text/plain)
- 非常漂亮的網頁一樣的頁面。
(text/html)
- 包括回覆另一封郵件的層層嵌套的內容。
(multipart/mixed)
- 帶附件的內容,比如一張圖片。
(multipart/mixed)
+(image/jpeg)
當然,這不是全部,只是有代表性的幾種文檔類型。最重要的是知道:
所有超出簡單文字或網頁HTML之外的,全都是multiparts
。
最難理解的也是這個multiparts。
下面是最複雜的Multiparts郵件,包括了所有能包括的結構。其中每個方塊都有自己的Content Type
和Body
。
簡單點的結構圖:
[站外圖片上傳中...(image-8219fc-1548158814349)]
文字型結構圖:
multipart/mixed
|
+-- multipart/related
| |
| +-- multipart/alternative
| | |
| | +-- text/plain
| | +-- text/html
| |
| +-- image/gif
|
+-- application/msword
詳細一點的結構圖:
[站外圖片上傳中...(image-36fdd8-1548158814349)]
這裏是所有郵件能支持的Content Type
文檔類型:
-
text/plain
: 純文本,文件擴展名.txt -
text/html
: HTML文本,文件擴展名.htm和.html -
image/jpeg
: jpeg格式的圖片,文件擴展名.jpg -
image/gif
: GIF格式的圖片,文件擴展名.gif -
audio/x-wave
: WAVE格式的音頻,文件擴展名.wav -
audio/mpeg
: MP3格式的音頻,文件擴展名.mp3 -
video/mpeg
: MPEG格式的視頻,文件擴展名.mpg -
application/zip
: PK-ZIP格式的壓縮文件,文件擴展名.zip
編程上需要明確的是:要讀取嵌套結構,必須用遞歸的方法。
Content-Disposition 附件的存在方式
對於附件,有兩種存在方式:
-
inline
: 嵌入在文字裏的,比如HTML格式郵件中顯示的圖片 -
attachment
: 是附在結尾的,單獨的一部分
一般我們只需要處理attachment格式的附件,而inline的東西就讓它保存在inline裏吧。
郵件裏面要獲取這個部分的格式,需要找到這個參數:Content-Disposition
。其它並拍的參數還要Content-Type
和Content-ID
等。
Content-Transfer-Encoding 文本傳輸的編碼方式
這個只針對text/plain & text/html
類型的文本有用。
這個是每封郵件的必須數據,它必須要指出每段文本的傳輸編碼方式
,有的可以壓縮傳輸(base64),有的可以原文傳輸(8bit或7bit),有的可以內置base64圖片可直接打印(quoted-printable)。
正因爲每封郵件都可能採用不同的傳輸編碼策略,所以我們解析內容之前必須要判斷是哪種方式才能正確解碼爲原文的內容。
目前常見的傳輸編碼方式有:
-
8bit
或7bit
:這個最簡單,直接是原文,不需要轉碼。 -
base64
:內容全文用base64壓縮,所以需要用base64.b64decode()
庫函數來解碼。 -
quoted-printable
:另一種壓縮方式,需要用quopri.decodestring()
庫函數來解碼。
獲取當前內容的傳輸編碼方式的代碼如下:
encoding = part.get('Content-Transfer-Encoding')
其中part
可以是庫email.message.Message
的實例或者其中multipart多部分中的sub-part,都可以。