Рубрики
Без рубрики

почтовый ящик – Управление архивами электронной почты

Автор оригинала: Doug Hellmann.

Цель:

Работайте с сообщениями электронной почты в различных локальных файловых форматах.

Модуль mailbox определяет общий API для доступа к сообщениям электронной почты, хранящимся в форматах локального диска, включая:

  • Maildir
  • mbox
  • MH
  • Вавил
  • MMDF

Существуют базовые классы для Mailbox и Message , и каждый формат почтового ящика включает соответствующую пару подклассов для реализации деталей этого формата.

mbox

Формат mbox проще всего отображать в документации, поскольку он представляет собой полностью простой текст. Каждый почтовый ящик хранится в виде единого файла, в котором все сообщения объединяются. Каждый раз, когда встречается строка, начинающаяся с «From» («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.\n')
    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 
To: Recipient 
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 
To: Recipient 
Subject: Sample message 2

This is the second body.

Чтение почтового ящика mbox

Чтобы прочитать существующий почтовый ящик, откройте его и обращайтесь с объектом mbox как со словарем. Ключи – это произвольные значения, определенные экземпляром почтового ящика, и они не обязательно имеют смысл, кроме как внутренние идентификаторы для объектов сообщения.

mailbox_mbox_read.py

import mailbox

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

Открытый почтовый ящик поддерживает протокол итератора, но в отличие от настоящих объектов словаря итератор по умолчанию для почтового ящика работает со значениями значения вместо ключей .

$ 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 
To: Recipient 
Subject: Sample message 1

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

Maildir

Формат Maildir был создан для устранения проблемы одновременного изменения файла mbox. Вместо использования одного файла почтовый ящик организован как каталог, в котором каждое сообщение содержится в отдельном файле. Это также позволяет вкладывать почтовые ящики, поэтому API для почтового ящика Maildir расширен методами для работы с подпапками.

Создание почтового ящика 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.\n')
    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('\n***', 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 
To: Recipient 
Subject: Sample message 2

This is the second body.

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

*** Example/new/1521404460.M303200P41689Q1.hubert.local
From: Author 
To: Recipient 
Subject: Sample message 1

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

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

После того как они будут прочитаны, клиент может переместить их в подкаталог 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('\nAfter:')
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: []

Чтение почтового ящика Maildir

Чтение из существующего почтового ящика 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('\n***', 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 
To: Recipient 
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('\n{:#^30}\n'.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('\n{:#^30}\n'.format(''))

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

print('\n{:#^30}\n'.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 имеют методы для извлечения и изменения значений флагов. В этом примере показаны флаги сообщений в Пример

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')
        # Tell the mailbox to update the message.
        mbox[message_id]  message
finally:
    mbox.flush()
    mbox.close()

print('\nAfter:')
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')
        # Tell the mailbox to update the message.
        mbox[message_id]  message
finally:
    mbox.flush()
    mbox.close()

print('\nAfter:')
mbox  mailbox.Maildir('Example')
for message in mbox:
    print('{:6} "{}"'.format(message.get_flags(),
                             message['subject']))

Флаг F , добавленный в предыдущем примере, теряется, когда set_flags () заменяет флаги на S в этом примере.

$ python3 mailbox_maildir_set_flags.py

Before:
F      "Sample message 1"

After:
S      "Sample message 1"

Другие форматы

mailbox поддерживает несколько других форматов, но ни один из них не пользуется такой популярностью, как mbox или Maildir. MH – еще один формат многофайлового почтового ящика, используемый некоторыми почтовыми обработчиками. Babyl и MMDF – это однофайловые форматы с разными разделителями сообщений, чем

Смотрите также