14.4. mailbox — 管理 Email 规定文件
目的:处理各种本地文件格式的电子邮件。
mailbox
模块定义了用于访问以本地磁盘格式存储的电子邮件的通用API,包括:
- Maildir
- mbox
- MH
- Babyl
- MMDF
有Mailbox
和Message
的基类,每种邮箱格式都包括一对相应的子类,以实现该格式的详细信息。
mbox
mbox格式是纯文本格式,因此最容易在文档中显示。每个邮箱都存储为单个文件,所有邮件都串联在一起。每次遇到以“From”(“发件人”,后跟一个空格)开头的行,它将被视为新消息的开头。每当这些字符出现在消息正文中行的开头时,都可以通过在行前面添加“>” 来对其进行转义。
创建一个mbox邮箱
通过将文件名传递给构造函数来实例化mbox
类。如果文件不存在,则在使用add()
附加消息时创建该文件。
mailbox_mbox_create.py
import mailbox
import email.utils
from_addr = email.utils.formataddr(('Author',
'author@example.com'))
to_addr = email.utils.formataddr(('Recipient',
'recipient@example.com'))
payload = '''This is the body.
From (will not be escaped).
There are 3 lines.
'''
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(payload)
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..')
mbox.add(msg)
mbox.flush()
finally:
mbox.unlock()
print(open('example.mbox', 'r').read())
该脚本的结果是一个包含两个电子邮件的新邮箱文件。
$ python3 mailbox_mbox_create.py
From MAILER-DAEMON Sun Mar 18 20:20:59 2018
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.
From MAILER-DAEMON Sun Mar 18 20:20:59 2018
From: Author <author@example.com>
To: Recipient <recipient@example.com>
Subject: Sample message 2
This is the second body.
Reading an mbox Mailbox
要读取现有邮箱,请打开它并将mbox
对象视为字典。密钥是邮箱实例定义的任意值,除了作为邮件对象的内部标识符之外,没有其他意义。
mailbox_mbox_read.py
import mailbox
mbox = mailbox.mbox('example.mbox')
for message in mbox:
print(message['subject'])
打开的邮箱支持迭代器协议,但是与真正的字典对象不同,邮箱的默认迭代器在 values 而不是 keys 上运行。
$ python3 mailbox_mbox_read.py
Sample message 1
Sample message 2
从mbox邮箱中删除邮件
要从 mbox 文件中删除现有消息,请使用其键和remove()
或使用del
。
mailbox_mbox_remove.py
import mailbox
mbox = mailbox.mbox('example.mbox')
mbox.lock()
try:
to_remove = []
for key, msg in mbox.iteritems():
if '2' in msg['subject']:
print('Removing:', key)
to_remove.append(key)
for key in to_remove:
mbox.remove(key)
finally:
mbox.flush()
mbox.close()
print(open('example.mbox', 'r').read())
lock()
和unlock()
方法用于防止问题同时访问文件,并且flush()
强制将更改写入磁盘。
$ python3 mailbox_mbox_remove.py
Removing: 1
From MAILER-DAEMON Sun Mar 18 20:20:59 2018
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.
Maildir
创建 Maildir 格式是为了消除同时修改 mbox 文件的问题。邮箱不使用单个文件,而是组织为目录,其中每个邮件都包含在其自己的文件中。这还允许嵌套邮箱,因此 Maildir 邮箱的 API 扩展了与子文件夹一起使用的方法。
创建Maildir邮箱
创建Maildir
和mbox
的唯一真正区别是构造函数的参数是目录名而不是文件名。和以前一样,如果邮箱不存在,则会在添加邮件时创建它。
mailbox_maildir_create.py
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'))
payload = '''This is the body.
From (will not be escaped).
There are 3 lines.
'''
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(payload)
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..')
mbox.add(msg)
mbox.flush()
finally:
mbox.unlock()
for dirname, subdirs, files in os.walk('Example'):
print(dirname)
print(' Directories:', subdirs)
for name in files:
fullname = os.path.join(dirname, name)
print('.***', fullname)
print(open(fullname).read())
print('*' * 20)
将邮件添加到邮箱后,它们将转到new
子目录。
警告
尽管从多个进程中写入同一maildir是安全的,但是
add()
并不是线程安全的。使用信号量或其他锁定设备,以防止同一进程的多个线程同时修改邮箱。
$ python3 mailbox_maildir_create.py
Example
Directories: ['new', 'cur', 'tmp']
Example/new
Directories: []
*** Example/new/1521404460.M306174P41689Q2.hubert.local
From: Author <author@example.com>
To: Recipient <recipient@example.com>
Subject: Sample message 2
This is the second body.
********************
*** Example/new/1521404460.M303200P41689Q1.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/cur
Directories: []
Example/tmp
Directories: []
读取它们之后,客户端可以使用MaildirMessage
的set_subdir()
方法将它们移动到cur
子目录中。
mailbox_maildir_set_subdir.py
import mailbox
import os
print('Before:')
mbox = mailbox.Maildir('Example')
mbox.lock()
try:
for message_id, message in mbox.iteritems():
print('{:6} "{}"'.format(message.get_subdir(),
message['subject']))
message.set_subdir('cur')
# Tell the mailbox to update the message.
mbox[message_id] = message
finally:
mbox.flush()
mbox.close()
print('.After:')
mbox = mailbox.Maildir('Example')
for message in mbox:
print('{:6} "{}"'.format(message.get_subdir(),
message['subject']))
print()
for dirname, subdirs, files in os.walk('Example'):
print(dirname)
print(' Directories:', subdirs)
for name in files:
fullname = os.path.join(dirname, name)
print(fullname)
尽管maildir包含“ tmp
”目录,但set_subdir()
的唯一有效参数是“ cur
”和“ new
” 。
$ python3 mailbox_maildir_set_subdir.py
Before:
new "Sample message 2"
new "Sample message 1"
After:
cur "Sample message 2"
cur "Sample message 1"
Example
Directories: ['new', 'cur', 'tmp']
Example/new
Directories: []
Example/cur
Directories: []
Example/cur/1521404460.M306174P41689Q2.hubert.local
Example/cur/1521404460.M303200P41689Q1.hubert.local
Example/tmp
Directories: []
Reading a Maildir Mailbox
从现有 Maildir 邮箱中进行读取就像 mbox 邮箱一样。
mailbox_maildir_read.py
import mailbox
mbox = mailbox.Maildir('Example')
for message in mbox:
print(message['subject'])
不能保证以任何特定顺序读取消息。
$ python3 mailbox_maildir_read.py
Sample message 2
Sample message 1
从Maildir邮箱中删除邮件
要从Maildir邮箱中删除现有邮件,请将其密钥传递到remove()
或使用del
。
mailbox_maildir_remove.py
import mailbox
import os
mbox = mailbox.Maildir('Example')
mbox.lock()
try:
to_remove = []
for key, msg in mbox.iteritems():
if '2' in msg['subject']:
print('Removing:', key)
to_remove.append(key)
for key in to_remove:
mbox.remove(key)
finally:
mbox.flush()
mbox.close()
for dirname, subdirs, files in os.walk('Example'):
print(dirname)
print(' Directories:', subdirs)
for name in files:
fullname = os.path.join(dirname, name)
print('.***', fullname)
print(open(fullname).read())
print('*' * 20)
无法计算邮件的密钥,因此请同时使用items()
或iteritems()
从邮箱同时检索密钥和消息对象。
$ python3 mailbox_maildir_remove.py
Removing: 1521404460.M306174P41689Q2.hubert.local
Example
Directories: ['new', 'cur', 'tmp']
Example/new
Directories: []
Example/cur
Directories: []
*** Example/cur/1521404460.M303200P41689Q1.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
类的方法进行管理。呼叫者可以列出,检索,创建和删除给定邮箱的子文件夹。
mailbox_maildir_folders.py
import mailbox
import os
def show_maildir(name):
os.system('find {} -print'.format(name))
mbox = mailbox.Maildir('Example')
print('Before:', mbox.list_folders())
show_maildir('Example')
print('.{:#^30}.'.format(''))
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('.{:#^30}.'.format(''))
subfolder.add_folder('second_level')
print('second_level created:', subfolder.list_folders())
show_maildir('Example')
print('.{:#^30}.'.format(''))
subfolder.remove_folder('second_level')
print('second_level removed:', subfolder.list_folders())
show_maildir('Example')
文件夹的目录名称是通过在文件夹名称前加上句点(.
)来构造的。
$ python3 mailbox_maildir_folders.py
Example
Example/new
Example/cur
Example/cur/1521404460.M303200P41689Q1.hubert.local
Example/tmp
Example
Example/.subfolder
Example/.subfolder/maildirfolder
Example/.subfolder/new
Example/.subfolder/cur
Example/.subfolder/tmp
Example/new
Example/cur
Example/cur/1521404460.M303200P41689Q1.hubert.local
Example/tmp
Example
Example/.subfolder
Example/.subfolder/.second_level
Example/.subfolder/.second_level/maildirfolder
Example/.subfolder/.second_level/new
Example/.subfolder/.second_level/cur
Example/.subfolder/.second_level/tmp
Example/.subfolder/maildirfolder
Example/.subfolder/new
Example/.subfolder/cur
Example/.subfolder/tmp
Example/new
Example/cur
Example/cur/1521404460.M303200P41689Q1.hubert.local
Example/tmp
Example
Example/.subfolder
Example/.subfolder/maildirfolder
Example/.subfolder/new
Example/.subfolder/cur
Example/.subfolder/tmp
Example/new
Example/cur
Example/cur/1521404460.M303200P41689Q1.hubert.local
Example/tmp
Before: []
##############################
subfolder created: ['subfolder']
subfolder contents: []
##############################
second_level created: ['second_level']
##############################
second_level removed: []
消息标志
邮箱中的邮件具有用于跟踪各个方面的标志,例如邮件是否已被阅读,阅读者将其标记为重要或以后标记为删除。标志以特定于格式的字母代码序列存储,Message
类具有检索和更改标志值的方法。此示例在添加标记以表明该消息被视为重要之前,在Example
maildir中显示消息上的标志。
mailbox_maildir_add_flag.py
import mailbox
print('Before:')
mbox = mailbox.Maildir('Example')
mbox.lock()
try:
for message_id, message in mbox.iteritems():
print('{:6} "{}"'.format(message.get_flags(),
message['subject']))
message.add_flag('F')
# 告诉邮箱更新消息
mbox[message_id] = message
finally:
mbox.flush()
mbox.close()
print('.After:')
mbox = mailbox.Maildir('Example')
for message in mbox:
print('{:6} "{}"'.format(message.get_flags(),
message['subject']))
默认情况下,消息没有标志。添加标志会更改内存中的消息,但不会更新磁盘上的消息。若要更新磁盘上的邮件,请使用其现有标识符将邮件对象存储在邮箱中。
$ python3 mailbox_maildir_add_flag.py
Before:
"Sample message 1"
After:
F "Sample message 1"
使用add_flag()
添加标志会保留所有现有标志。使用set_flags()
覆盖任何现有的标志集,并将其替换为传递给该方法的新值。
mailbox_maildir_set_flags.py
import mailbox
print('Before:')
mbox = mailbox.Maildir('Example')
mbox.lock()
try:
for message_id, message in mbox.iteritems():
print('{:6} "{}"'.format(message.get_flags(),
message['subject']))
message.set_flags('S')
# 告诉邮箱更新消息
mbox[message_id] = message
finally:
mbox.flush()
mbox.close()
print('.After:')
mbox = mailbox.Maildir('Example')
for message in mbox:
print('{:6} "{}"'.format(message.get_flags(),
message['subject']))
在本示例中,当set_flags()
将标志替换为S
时,上一示例添加的F
标志丢失。
$ python3 mailbox_maildir_set_flags.py
Before:
F "Sample message 1"
After:
S "Sample message 1"
其他格式
mailbox
支持其他几种格式,但没有一种比mbox或Maildir受欢迎。 MH是某些邮件处理程序使用的另一种多文件邮箱格式。 Babyl和MMDF是单文件格式,具有与mbox不同的消息分隔符。单文件格式支持与mbox相同的API,MH包括Maildir类中与文件夹相关的方法。
另请参见
-邮箱的标准库文档
-邮箱的Python 2至3移植说明
-来自qmail的mbox联机帮助页– mbox格式的文档。
-来自qmail的Maildir联机帮助页– Maildir格式的文档。
-电子邮件
-电子邮件
模块。
-[imaplib
](pymotw.com/3/imaplib/index.html#mo...“ imaplib:IMAP4客户端库”)– –imaplib
模块可以在IMAP服务器上使用已保存的电子邮件。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。