Python 用IMAP接收郵件


一、簡介
IMAP(Internet Message Access Protocol),這個協議與POP一樣,也是從郵件服務器上下載郵件到本機,不過IMAP比POP的功能要更加強大些,IMAP除支持POP所有功能外,還支持以下功能:

  • 多個郵件文件夾(收件箱、發件箱、垃圾郵件...)
  • IMAP服務器上進行標記如:Seen, Replied, Read, Deleted
  • 在服務器端的文件夾之間拷貝和移動郵件
  • ...

  在IMAP的各版本中,最流行的是IMAP4。我們就使用IMAP4
  由於,我需要搜索是否有未讀郵件,也就是利用郵件服務器的Flag,所以IMAP是非常適合的,我的程序就利用的是IMAP。
  在Python的標准庫包含一個imaplib模塊,可以利用這個模塊。但是,這個模塊的缺陷就是把大量解析的工作留給客戶端程序員。
二、IMAPClient
  IMAPClient是一個非常受歡迎的IMAPCLient包,這個模塊不在標准Python庫中。IMAPClient包是由一名叫做Menno Smits的Python程序員編寫的。官網網址:http://imapclient.freshfoo.com/。可以在這里查看手冊文檔。這個包是基於標准庫imaplib,不過要更強大。下面我們來介紹下怎樣安裝。
1. virtualenv
  說實話,我本人對virtualenv的理解也不透徹,以字面上來理解為虛擬環境。可以把一些模塊、包安裝在特定的virtualenv里,一旦安裝了virtualenv,你就創建任意多個自組織的虛擬python環境,在這個環境里,可以安裝、下載包。
  好吧,廢話就不多說,直接說方法。
  這里是virtualenv的詳細說明,上面介紹了非常詳細的安裝方法,按照我自己的經驗,可以簡化為以下步驟:

$ [sudo] pip install virtualenv
$ [sudo] pip install https://github.com/pypa/virtualenv/tarball/develop
$ curl -O https://pypi.python.org/packages/source/v/virtualenv/ virtualenv-X.X.tar.gz
$ tar xvfz virtualenv-X.X.tar.gz
$ cd virtualenv-X.X
$ [sudo] python setup.py install

  注意,上面下載的 virtualenv-X.X.tar.gz 中的X是型號,需要把它改成數字,詳細版本類型可以參考:https://pypi.python.org/packages/source/v/virtualenv/

  這樣,virtualenv已經安裝好。下面需要創建虛擬環境實例,步驟如下:
$ virtualenv --no-site-packages myenv
$ cd myenv
2. 安裝IMAPClient
  myenv 為自己定義的虛擬環境的名字。這樣,我們已經在myenv里面,接下來就可一安裝IMAPClient包了。步驟如下:
$ sudo pip install imapclient
$ python -c 'import imapclient'

  此時,可以在python下使用imapclient模塊,但是不能在python3下使用,在網上查了一些資料,尤其是看了上面的那個介紹virtualenv的網頁,沒找到有用的,但是,回頭發現,這個imapclient是好使的了,不用進入gmapenv,直接使用即可,got it!
  注意,上面用到了pip工具,如果沒有的話一定要安裝啊。
$ sudo apt-get install pip

三、開始正式學習IMAP
1. 因為可能會出現中文,因此在程序的最上面,必須加上如下代碼:

#-*- encoding: utf-8 -*-
#-*- encoding: gbk -*-

2. 所需模塊

import getpass, email, sys
from imapclient import IMAPClient

3. 連接服務、登錄賬戶
  這一步也沒什么好講的。代碼如下:

# 通過以下方式連接smtp服務器,沒有考慮異常情況,詳細請參考官方文檔
c = IMAPClient(hostname = 'imap.gmail.com', ssl= True) 
try:
    c.login(username, passwd) #登錄個人帳號
except c.Error:
    print('Could not log in')
    sys.exit(1)

4. 進入收件箱,查看未讀郵件

c.select_folder('INBOX', readonly = True) 
result = c.search('UNSEEN')

  利用select_folder()函數進行文件夾,'INBOX'為收件箱,readonly = True 表明只讀並不修改任何信息
  利用search()函數選擇想要的郵件,'UNSEEN'是郵件的flag,關於郵件的flag就不特別說明了,返回郵件的message-id
5. 有了未讀郵件的ID(result),下面利用fetch()函數將郵件取來(下載到本機)

msgdict = c.fetch(result, ['BODY.PEEK[]'] )

  通過fetch()函數取得郵件內容,fetch()的詳細介紹請見這里
      fetch(self, message, data) 其中self參數可忽略,message為message_id, data 的作用是抓取message中的哪些部分。  官方文檔中沒有給出data的其他可選的參數,我一開始怎么都不找到,最終在stackoverflow中進行提問,一位大哥把這個文檔介紹給我,在 6.4.5 FETCH Command 。這里面非常詳細的介紹了各個函數的各種細節,當然也可以查到data其他可選的參數 6.4.5 表示的是原書的節。特別感謝這位哥們,人類的力量是無窮的啊!

  我們只需要'BODY.PEEK[]'即可。
6. 已經把郵件取出,下面開始解析郵件

for message_id, message in msgdict.items():
e = email.message_from_string(message['BODY[]']) # 生成Message類型

7. 得到的 e 即為Message類型的郵件,先面開始將又將中解析出'From', 'Subject'

  還記得上面在POP講解中,我們遇到的不能顯示中文的問題嗎?在IMAP中仍會出現,下面就講解解決辦法
  由於'From', 'Subject' header有可能有中文,必須把它轉化為中文,在這個點上,耽誤了我很長時間,最終在網上查到了一個方法:http://blog.csdn.net/bonnshore/article/details/8729984 雖然不是很明白,但是能把問題解決就是王道。代碼如下:

subject = email.header.make_header(email.header.decode_header(e['SUBJECT'])) #必須保證包含subject
mail_from = email.header.make_header(email.header.decode_header(e['From']))

8. 從Message e中解析出content正文
  同上一篇的POP一樣,根據get_payload()返回的不同類型,選擇解析方法,代碼如下:

maintype = e.get_content_maintype()
if maintype == 'multipart':
    for part in e.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()

# 此時,需要把content轉化成中文,利用如下方法:    
try:
    mail_content = mail_content.decode('gbk')
except UnicodeDecodeError:
    print('decode error')
    sys.exit(1)

9. 至此,我們已經完成了查看是否有未讀郵件。如果有的話將未讀郵件的'From', 'Subject', content解析出來。正如上面完成的 mail_from, subject, mail_content一樣,現在可以完美的顯示,即使有中文!

四、完整代碼

#-*- encoding: utf-8 -*-
#-*- encoding: gbk -*-

# 因為可能會用到中文,所以必須有上面的這兩句話

# 引入模塊及IMAPClient類
import getpass, email, sys
from imapclient import IMAPClient

hostname = 'imap.gmail.com' #gmail的smtp服務器網址
username = 'myUserName@gmail.com'
passwd = '***'

c = IMAPClient(hostname, ssl= True) # 通過一下方式連接smtp服務器,沒有考慮異常情況,詳細請參考官方文檔
try:
    c.login(username, passwd) #登錄個人帳號
except c.Error:
    print('Could not log in')
    sys.exit(1)
else:
    c.select_folder('INBOX', readonly = True) 
# 利用select_folder()函數進行文件夾,'INBOX'為收件箱,readonly = True 表明只讀並不修改任何信息
result = c.search('UNSEEN') msgdict = c.fetch(result, ['BODY.PEEK[]'] ) # 現在已經把郵件取出來了,下面開始解析郵件 for message_id, message in msgdict.items(): e = email.message_from_string(message['BODY[]']) # 生成Message類型
     # 由於'From', 'Subject' header有可能有中文,必須把它轉化為中文 subject = email.header.make_header(email.header.decode_header(e['SUBJECT'])) mail_from = email.header.make_header(email.header.decode_header(e['From']))      
     # 解析郵件正文 maintype = e.get_content_maintype() if maintype == 'multipart': for part in e.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()
     # 此時,需要把content轉化成中文,利用如下方法: try: mail_content = mail_content.decode('gbk') except UnicodeDecodeError: print('decode error') sys.exit(1) else: print('new message') print('From: ', mail_from) print('Subject: ', subject) getstr = input('if you wanna read it, input y: ') if getstr.startswith('y'): print('-'*10, 'mail content', '-'*10) print(mail_content.replace('<br>', '\n')) print('-'*10, 'mail content', '-'*10) finally:
  c.logout()

五、總結
  至此,我們已經學習了利用Python編寫郵件服務的所有非常基本的內容,由於我的需求不是很高,目標不是做成一個功能強大的郵箱客戶端,所以諸如:MIME、附件、圖片等功能都沒有學習,當然也沒有介紹。
  因為我們現在接收的郵件,大多數都是MIME格式的,不過上文的包含了點解析MIME格式郵件的代碼。詳細請參考《Foundations of Python3 Network Programming. 2nd Edition》Chaper E-mail Composition and Decoding。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM