Python 用IMAP接收郵件

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

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

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

$ [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 installgit

  注意,上面下載的 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'github

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

3、開始正式學習IMAP
1. 由於可能會出現中文,所以在程序的最上面,必須加上以下代碼:
apache

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

2. 所需模塊服務器

import getpass, email, sys
from imapclient import IMAPClient

3. 鏈接服務、登陸帳戶
  這一步也沒什麼好講的。代碼以下:curl

# 經過如下方式鏈接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同樣,如今能夠完美的顯示,即便有中文!

4、完整代碼

#-*- 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()

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

相關文章
相關標籤/搜索