mailbox – メールアーカイブのアクセスと操作

目的:様々なローカルファイルフォーマットのメールを扱う
利用できるバージョン:1.4 以上

mailbox モジュールはローカルファイルフォーマットに格納されたメールのメッセージにアクセスする共通 API を定義します。

  • Maildir
  • mbox
  • MH
  • Babyl
  • MMDF

MailboxMessage という2つのベースクラスがあります。各メールボックスのフォーマットは、そのフォーマットの詳細を実装した対応するサブクラスのペアを含みます。

mbox

mbox フォーマットは全てプレーンテキストなので文章で説明することが最も簡単です。各メールボックスは全てのメッセージを連結した1つのファイルとして格納されます。”From ” (From の後にスペースが1個続く) で始まる行に遭遇すると、新たなメッセージの始まりとして扱われます。メッセージ本文の行頭に “From ” が現れたときその行の接頭辞として “>” でエスケープします。

mbox メールボックスを作成する

email.mbox クラスのコンストラクタにファイル名を渡してインスタンス化してください。もしそのファイルが存在しなかったら add() を使用してそのメールボックスにメッセージを追加するときにメールボックスが作成されます。

import mailbox
import email.utils

from_addr = email.utils.formataddr(('Author', 'author@example.com'))
to_addr = email.utils.formataddr(('Recipient', 'recipient@example.com'))

mbox = mailbox.mbox('example.mbox')
mbox.lock()
try:
    msg = mailbox.mboxMessage()
    msg.set_unixfrom('author Sat Feb  7 01:05:34 2009')
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Subject'] = 'Sample message 1'
    msg.set_payload('This is the body.\nFrom (should be escaped).\nThere are 3 lines.\n')
    mbox.add(msg)
    mbox.flush()

    msg = mailbox.mboxMessage()
    msg.set_unixfrom('author')
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Subject'] = 'Sample message 2'
    msg.set_payload('This is the second body.\n')
    mbox.add(msg)
    mbox.flush()
finally:
    mbox.unlock()

print open('example.mbox', 'r').read()

このスクリプトの実行結果は2つのメッセージを持つ新たなメールボックスが作成されます。

$ python mailbox_mbox_create.py
From MAILER-DAEMON Sun Feb 17 16:32:33 2013
From: Author <author@example.com>
To: Recipient <recipient@example.com>
Subject: Sample message 1

This is the body.
>From (should be escaped).
There are 3 lines.

From MAILER-DAEMON Sun Feb 17 16:32:33 2013
From: Author <author@example.com>
To: Recipient <recipient@example.com>
Subject: Sample message 2

This is the second body.

mbox メールボックスを読み込む

既存のメールボックスを読むには、そのメールボックスをオープンして辞書のように mbox オブジェクトを扱います。メッセージオブジェクトはメールボックスインスタンスが定義した任意の値をキーに取りますが、そのオブジェクトの内部的な識別子として以外に意味はありません。

import mailbox

mbox = mailbox.mbox('example.mbox')
for message in mbox:
    print message['subject']

オープンしたメールボックスは繰り返し処理することができますが、辞書とは違い、メールボックスのデフォルトイテレータは キー の代わりに で動作します。

$ python mailbox_mbox_read.py
Sample message 1
Sample message 2

mbox メールボックスからメッセージを削除する

mbox ファイルから既存メッセージを削除するには remove() メソッドにそのキーを渡すか del を使用してください。

import mailbox

mbox = mailbox.mbox('example.mbox')
to_remove = []
for key, msg in mbox.iteritems():
    if '2' in msg['subject']:
        print 'Removing:', key
        to_remove.append(key)
mbox.lock()
try:
    for key in to_remove:
        mbox.remove(key)
finally:
    mbox.flush()
    mbox.close()

print open('example.mbox', 'r').read()

メールボックスのファイルに対する同時アクセス問題を防ぐために lock()unlock() を使用していることに注意してください。そして、強制的にその変更内容をディスクへ書き込むために flush() します。

$ python mailbox_mbox_remove.py
Removing: 1
From MAILER-DAEMON Sun Feb 17 16:32:33 2013
From: Author <author@example.com>
To: Recipient <recipient@example.com>
Subject: Sample message 1

This is the body.
>From (should be escaped).
There are 3 lines.

Maildir

1つの mbox ファイルの同時アクセスによる変更の問題を解決するために Maildir フォーマットが作成されました。1つのファイルを使用するのではなく、メールボックスをディレクトリとして構成して各メッセージをそのディレクトリ内に保存します。また Maildir メールボックスの API はサブフォルダでも動作するようにメソッドが拡張されているのでネストされたメールボックスも扱うことができます。

Maildir メールボックスを作成する

Maildir と mbox の使用における実際の違いは email.Maildir オブジェクトをインスタンス化するためにコンストラクタへメールボックスを含むディレクトリを渡す必要がある点のみになります。mbox のセクションで説明した通り、もしディレクトリが存在しなかったら add() を使用してメッセージを追加したときにメールボックスが作成されます。

import mailbox
import email.utils
import os

from_addr = email.utils.formataddr(('Author', 'author@example.com'))
to_addr = email.utils.formataddr(('Recipient', 'recipient@example.com'))

mbox = mailbox.Maildir('Example')
mbox.lock()
try:
    msg = mailbox.mboxMessage()
    msg.set_unixfrom('author Sat Feb  7 01:05:34 2009')
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Subject'] = 'Sample message 1'
    msg.set_payload('This is the body.\nFrom (will not be escaped).\nThere are 3 lines.\n')
    mbox.add(msg)
    mbox.flush()

    msg = mailbox.mboxMessage()
    msg.set_unixfrom('author Sat Feb  7 01:05:34 2009')
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Subject'] = 'Sample message 2'
    msg.set_payload('This is the second body.\n')
    mbox.add(msg)
    mbox.flush()
finally:
    mbox.unlock()

for dirname, subdirs, files in os.walk('Example'):
    print dirname
    print '\tDirectories:', subdirs
    for name in files:
        fullname = os.path.join(dirname, name)
        print
        print '***', fullname
        print open(fullname).read()
        print '*' * 20

メールボックスへメッセージを追加すると “new” サブディレクトリにそのメッセージが追加されます。メールクライアントがそのメッセージを “読み込む” と “cur” サブディレクトリへ移動されます。

Warning

複数プロセスから同じ Maildir へ書き込むことは安全ですが add() はスレッドセーフではありません。そのため、同一プロセスの複数スレッドからメールボックスへ同時アクセスによる変更を防ぐためにセマフォ又はロックの仕組みを使用するようにしてください。

$ python mailbox_maildir_create.py
Example
        Directories: ['cur', 'new', 'tmp']
Example/cur
        Directories: []
Example/new
        Directories: []

*** Example/new/1361118753.M257292P70977Q1.hubert.local
From: Author <author@example.com>
To: Recipient <recipient@example.com>
Subject: Sample message 1

This is the body.
From (will not be escaped).
There are 3 lines.

********************

*** Example/new/1361118753.M258130P70977Q2.hubert.local
From: Author <author@example.com>
To: Recipient <recipient@example.com>
Subject: Sample message 2

This is the second body.

********************
Example/tmp
        Directories: []

Maildir メールボックスを読み込む

やはり mbox と同じように既存の Maildir メールボックスから読み込んでください。

import mailbox

mbox = mailbox.Maildir('Example')
for message in mbox:
    print message['subject']

メッセージは指定した特定の順番で読めないことに注意してください。

$ python mailbox_maildir_read.py
Sample message 2
Sample message 1

Maildir メールボックスからメッセージを削除する

Maildir メールボックスから既存メッセージを削除するには remove() メソッドにそのキーを渡すか del を使用してください。

import mailbox
import os

mbox = mailbox.Maildir('Example')
to_remove = []
for key, msg in mbox.iteritems():
    if '2' in msg['subject']:
        print 'Removing:', key
        to_remove.append(key)
mbox.lock()
try:
    for key in to_remove:
        mbox.remove(key)
finally:
    mbox.flush()
    mbox.close()

for dirname, subdirs, files in os.walk('Example'):
    print dirname
    print '\tDirectories:', subdirs
    for name in files:
        fullname = os.path.join(dirname, name)
        print
        print '***', fullname
        print open(fullname).read()
        print '*' * 20
$ python mailbox_maildir_remove.py
Removing: 1361118753.M258130P70977Q2.hubert.local
Example
        Directories: ['cur', 'new', 'tmp']
Example/cur
        Directories: []
Example/new
        Directories: []

*** Example/new/1361118753.M257292P70977Q1.hubert.local
From: Author <author@example.com>
To: Recipient <recipient@example.com>
Subject: Sample message 1

This is the body.
From (will not be escaped).
There are 3 lines.

********************
Example/tmp
        Directories: []

Maildir フォルダ

Maildir メールボックスのサブディレクトリ又は フォルダ は Maildir クラスのメソッドを通して直接管理できます。メソッドを呼び出すと、所定のメールボックスのサブフォルダに対する一覧表示、取り出し、作成、削除ができます。

import mailbox
import os

def show_maildir(name):
    os.system('find %s -print' % name)

mbox = mailbox.Maildir('Example')
print 'Before:', mbox.list_folders()
show_maildir('Example')

print
print '#' * 30
print

mbox.add_folder('subfolder')
print 'subfolder created:', mbox.list_folders()
show_maildir('Example')

subfolder = mbox.get_folder('subfolder')
print 'subfolder contents:', subfolder.list_folders()

print
print '#' * 30
print

subfolder.add_folder('second_level')
print 'second_level created:', subfolder.list_folders()
show_maildir('Example')

print
print '#' * 30
print

subfolder.remove_folder('second_level')
print 'second_level removed:', subfolder.list_folders()
show_maildir('Example')

フォルダのディレクトリ名は . から始まるフォルダ名で構成されます。

$ python mailbox_maildir_folders.py
Example
Example/cur
Example/new
Example/new/1361118753.M257292P70977Q1.hubert.local
Example/tmp
Example
Example/.subfolder
Example/.subfolder/cur
Example/.subfolder/maildirfolder
Example/.subfolder/new
Example/.subfolder/tmp
Example/cur
Example/new
Example/new/1361118753.M257292P70977Q1.hubert.local
Example/tmp
Example
Example/.subfolder
Example/.subfolder/.second_level
Example/.subfolder/.second_level/cur
Example/.subfolder/.second_level/maildirfolder
Example/.subfolder/.second_level/new
Example/.subfolder/.second_level/tmp
Example/.subfolder/cur
Example/.subfolder/maildirfolder
Example/.subfolder/new
Example/.subfolder/tmp
Example/cur
Example/new
Example/new/1361118753.M257292P70977Q1.hubert.local
Example/tmp
Example
Example/.subfolder
Example/.subfolder/cur
Example/.subfolder/maildirfolder
Example/.subfolder/new
Example/.subfolder/tmp
Example/cur
Example/new
Example/new/1361118753.M257292P70977Q1.hubert.local
Example/tmp
Before: []

##############################

subfolder created: ['subfolder']
subfolder contents: []

##############################

second_level created: ['second_level']

##############################

second_level removed: []

その他のフォーマット

MH は幾つかのメールハンドラで使用される複数ファイルの別のメールボックスフォーマットです。Babyl や MMDF は mbox とは違うメッセージセパレータを持つ1ファイルフォーマットです。こういったフォーマットは mbox や Maildir ほど一般的ではないと思います。Babyl や MMDF は mbox と同じ API を、MH は Maildir クラスにあるフォルダ関連のメソッドをサポートします。

See also

mailbox
本モジュールの標準ライブラリドキュメント
mbox manpage from qmail
http://www.qmail.org/man/man5/mbox.html
maildir manpage from qmail
http://www.qmail.org/man/man5/maildir.html
email
email モジュール
mhlib
mhlib モジュール
Bookmark and Share