mailbox - Manipola Archivi Email
Scopo: Lavora con messaggi email in vari formati di file locali
Il modulo mailbox definisce una API comune per accedere a messaggi email conservati in formati disco locale, inclusi:
- Maildir
- mbox
- MH
- Babyl
- MMDF
Ci sono classi base per Mailbox
e Message
, e ciascun formato di casella di posta include un paio di sottoclassi corrispondenti per implementare i dettagli per quel formato.
mbox
Il formato mbox è quello più semplice da mostrare nella documentazione, visto è interamente in testo semplice. Ogni casella di posta è conservata in un singolo file, con tutti i messaggi concatenati assieme. Ogni volta che viene rilevata una riga che inizia con "From
" ("From" seguito da un singolo spazio) essa viene trattata come l'inizio di un nuovo messaggio. Ogni volta che detti caratteri appaiono all'inizio di una riga di corpo del messaggio vengono prefissati dalla sequenza di escape ">
".
Creare una Casella di Posta mbox
Si istanzi la classe mbox
passando il nome del file al costruttore. Se il file non esiste, viene creato, quando si utilizza add()
per accodare i messaggi.
# mailbox_mbox_create.py
import mailbox
import email.utils
from_addr = email.utils.formataddr(('Autore',
'autore@esempio.com'))
to_addr = email.utils.formataddr(('Destinatario',
'destinatario@esempio.com'))
payload = '''Questo e' il corpo.
From (sara' prefissato da sequenza di escape).
Ci sono 3 righe.
'''
mbox = mailbox.mbox('esempio.mbox')
mbox.lock()
try:
msg = mailbox.mboxMessage()
msg.set_unixfrom('autore Sat Feb 7 01:05:34 2009')
msg['From'] = from_addr
msg['To'] = to_addr
msg['Subject'] = 'Messaggio campione 1'
msg.set_payload(payload)
mbox.add(msg)
mbox.flush()
msg = mailbox.mboxMessage()
msg.set_unixfrom('autore')
msg['From'] = from_addr
msg['To'] = to_addr
msg['Subject'] = 'Messaggio campione 2'
msg.set_payload("Questo e' il secondo corpo.\n")
mbox.add(msg)
mbox.flush()
finally:
mbox.unlock()
print(open('esempio.mbox', 'r').read())
Il risultato dello script è un nuovo file di casella di posta con due messaggi email.
$ python3 mailbox_mbox_create.py From MAILER-DAEMON Mon Nov 11 08:01:54 2019 From: Autore <autore@esempio.com> To: Destinatario <destinatario@esempio.com> Subject: Messaggio campione 1 Questo e' il corpo. >From (sara' prefissato da sequenza di escape). Ci sono 3 righe. From MAILER-DAEMON Mon Nov 11 08:01:54 2019 From: Autore <autore@esempio.com> To: Destinatario <destinatario@esempio.com> Subject: Messaggio campione 2 Questo e' il secondo corpo.
Leggere una Casella di Posta mbox
Per leggere una casella di posta esistente, la si apra e si tratti l'oggetto mbox
come un dizionario. Le chiavi sono valori arbitrari definiti dall'istanza mailbox
e non hanno un particolare significato se non quello di fungere da identificatori interni per gli oggetti messaggio.
# mailbox_mbox_read.py
import mailbox
mbox = mailbox.mbox('esempio.mbox')
for message in mbox:
print(message['subject'])
La casella di posta aperta supporta il protocollo iteratore, ma al contrario dei veri oggetti dizionario l'iteratore predefinito per la casella di posta lavora sui valori in luogo delle chiavi.
$ python3 mailbox_mbox_read.py Messaggio campione 1 Messaggio campione 2
Rimuovere Messaggi da una Casella di Posta mbox
Per rimuovere un messaggio esistente da un file mbox, si usi la sua chiave con remove()
oppure si usi del
.
# mailbox_mbox_remove.py
import mailbox
mbox = mailbox.mbox('esempio.mbox')
mbox.lock()
try:
to_remove = []
for key, msg in mbox.iteritems():
if '2' in msg['subject']:
print('Rimozione di:', key)
to_remove.append(key)
for key in to_remove:
mbox.remove(key)
finally:
mbox.flush()
mbox.close()
print(open('esempio.mbox', 'r').read())
I metodi lock()
e unlock()
sono utilizzati per prevenire problemi derivanti da accessi simultanei al file, e flush()
forza la scrittura su disco delle modifiche.
$ python3 mailbox_mbox_remove.py Rimozione di: 1 From MAILER-DAEMON Mon Nov 11 08:19:32 2019 From: Autore <autore@esempio.com> To: Destinatario <destinatario@esempio.com> Subject: Messaggio campione 1 Questo e' il corpo. >From (sara' prefissato da sequenza di escape). Ci sono 3 righe.
Maildir
Il formato Maildir fu creato per eliminare il problema di modifiche concorrenti a un file mbox. Invece che usare un file singolo, la casella di posta è organizzata come directory nella quale ciascun messaggio è contenuto nel suo proprio file. Questo consente anche un annidamento delle caselle di posta, quindi la API per una casella di posta Mailbox è estesa con metodi che lavorano con sottocartelle.
Creare una Casella di Posta Maildir
La sola reale differenza tra la creazione di una Maildir
rispetto a una mbox
è che l'argomento per il costruttore è un nome di directory invece che un nome di file. Come prima, se la casella di posta non esiste, viene creata quando vengono aggiunti i messaggi.
# mailbox_maildir_create.py
import mailbox
import email.utils
import os
from_addr = email.utils.formataddr(('Autore', 'autore@esempio.com'))
to_addr = email.utils.formataddr(('Destinatario', 'destinatario@esempio.com'))
payload = '''Questo e' il corpo.
From (non viene preceduta da sequenza di escape).
Ci sono tre righe.
'''
mbox = mailbox.Maildir('Esempio')
mbox.lock()
try:
msg = mailbox.mboxMessage()
msg.set_unixfrom('autore Sat Feb 7 01:05:34 2009')
msg['From'] = from_addr
msg['To'] = to_addr
msg['Subject'] = 'Messaggio campione 1'
msg.set_payload(payload)
mbox.add(msg)
mbox.flush()
msg = mailbox.mboxMessage()
msg.set_unixfrom('autore Sat Feb 7 01:05:34 2009')
msg['From'] = from_addr
msg['To'] = to_addr
msg['Subject'] = 'Messaggio campione 2'
msg.set_payload("Questo e' il secondo corpo.\n")
mbox.add(msg)
mbox.flush()
finally:
mbox.unlock()
for dirname, subdirs, files in os.walk('Esempio'):
print(dirname)
print(' Directory:', subdirs)
for name in files:
fullname = os.path.join(dirname, name)
print('\n***', fullname)
print(open(fullname).read())
print('*' * 20)
Quando i messaggi sono aggiunti alla casella di posta, vanno nella nuova sottodirectory.
add()
non è sicuro a livello di thread. Si utilizzi un semaforo o altro dispositivo di bloccaggio per prevenire modifiche simultanee alla casella di posta da thread multipli dello stesso processo.$ python3 mailbox_maildir_create.py Esempio Directory: ['new', 'tmp', 'cur'] Esempio/new Directory: [] *** Esempio/new/1573461573.M72514P9228Q1.robby-System-Product-Name From: Autore <autore@esempio.com> To: Destinatario <destinatario@esempio.com> Subject: Messaggio campione 1 Questo e' il corpo. From (non viene preceduta da sequenza di escape). Ci sono tre righe. ******************** *** Esempio/new/1573461573.M98691P9228Q2.robby-System-Product-Name From: Autore <autore@esempio.com> To: Destinatario <destinatario@esempio.com> Subject: Messaggio campione 2 Questo e' il secondo corpo. ******************** Esempio/tmp Directory: [] Esempio/cur Directory: []
Qualora fosse necessario, dopo avere letto i messaggi, è possibile spostarli dalla sottodirectory cur
tramite il metodo set_subdir()
di MaildirMessage
.
# mailbox_maildir_set_subdir.py
import mailbox
import os
print('Prima:')
mbox = mailbox.Maildir('Esempio')
mbox.lock()
try:
for message_id, message in mbox.iteritems():
print('{:6} "{}"'.format(message.get_subdir(),
message['subject']))
message.set_subdir('cur')
# Si chiede a mailbox di aggiornare il messaggio
mbox[message_id] = message
finally:
mbox.flush()
mbox.close()
print('\nDopo:')
mbox = mailbox.Maildir('Esempio')
for message in mbox:
print('{:6} "{}"'.format(message.get_subdir(),
message['subject']))
print()
for dirname, subdirs, files in os.walk('Esempio'):
print(dirname)
print(' Directory:', subdirs)
for name in files:
fullname = os.path.join(dirname, name)
print(fullname)
Anche se una casella maildir già include una directory "tmp
", i soli argomenti validi per set_subdir()
sono "cur
" e "new
".
$ python3 mailbox_maildir_set_subdir.py Prima: new "Messaggio campione 1" new "Messaggio campione 2" Dopo: cur "Messaggio campione 1" cur "Messaggio campione 2" Esempio Directory: ['new', 'tmp', 'cur'] Esempio/new Directory: [] Esempio/tmp Directory: [] Esempio/cur Directory: [] Esempio/cur/1573461573.M72514P9228Q1.robby-System-Product-Name Esempio/cur/1573461573.M98691P9228Q2.robby-System-Product-Name
Leggere una Casella di Posta Maildir
Il meccanismo di lettura di una Maildir esistente è uguale a quella di una casella mbox.
# mailbox_maildir_read.py
import mailbox
mbox = mailbox.Maildir('Esempio')
for message in mbox:
print(message['subject'])
Non è garantito che i messaggi vengano letti con un ordine particolare.
$ python3 mailbox_maildir_read.py Messaggio campione 2 Messaggio campione 1
Rimuovere Messaggi da una Casella di Posta Maildir
Per rimuovere un messaggio esistente da una Maildir, si passi la sua chiave a remove()
oppure si usi del
.
# mailbox_maildir_remove.py
import mailbox
import os
mbox = mailbox.Maildir('Esempio')
mbox.lock()
try:
to_remove = []
for key, msg in mbox.iteritems():
if '2' in msg['subject']:
print('Eliminazione di:', 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('Esempio'):
print(dirname)
print(' Directory:', subdirs)
for name in files:
fullname = os.path.join(dirname, name)
print('\n***', fullname)
print(open(fullname).read())
print('*' * 20)
Non vi è modo di calcolare la chiave per un messaggio, quindi si usi items()
o iteritems()
per recuperare contemporaneamente la chiave e l'oggetto messaggio dalla casella di posta.
$ python3 mailbox_maildir_remove.py Eliminazione di: 1573461573.M98691P9228Q2.robby-System-Product-Name Esempio Directory: ['new', 'tmp', 'cur'] Esempio/new Directory: [] Esempio/tmp Directory: [] Esempio/cur Directory: [] *** Esempio/cur/1573461573.M72514P9228Q1.robby-System-Product-Name From: Autore <autore@esempio.com> To: Destinatario <destinatario@esempio.com> Subject: Messaggio campione 1 Questo e' il corpo. From (non viene preceduta da sequenza di escape). Ci sono tre righe. ********************
Cartelle Maildir
Le sottodirectory o cartelle di una casella di posta Maildir possono essere gestite direttamente con i metodi della classe Maildir
. I chiamanti possono elencare, recuperare, creare e rimuovere sottocartelle per una data casella di posta.
# mailbox_maildir_folders.py
import mailbox
import os
def show_maildir(name):
os.system('find {} -print'.format(name))
mbox = mailbox.Maildir('Esempio')
print('Prima:', mbox.list_folders())
show_maildir('Esempio')
print('\n{:#^30}\n'.format(''))
mbox.add_folder('sottocartella')
print('creata sottocartella:', mbox.list_folders())
show_maildir('Esempio')
subfolder = mbox.get_folder('sottocartella')
print('sottocartella contiene:', subfolder.list_folders())
print('\n{:#^30}\n'.format(''))
subfolder.add_folder('secondo_livello')
print('creato secondo_livello:', subfolder.list_folders())
show_maildir('Esempio')
print('\n{:#^30}\n'.format(''))
subfolder.remove_folder('secondo_livello')
print('secondo_livello rimosso:', subfolder.list_folders())
show_maildir('Esempio')
Il nome della directory per la cartella viene costruito prefissando il nome della cartella con un punto (.
).
$ python3 mailbox_maildir_folders.py Prima: [] Esempio Esempio/new Esempio/tmp Esempio/cur Esempio/cur/1573461573.M72514P9228Q1.robby-System-Product-Name ############################## creata sottocartella: ['sottocartella'] Esempio Esempio/new Esempio/.sottocartella Esempio/.sottocartella/new Esempio/.sottocartella/tmp Esempio/.sottocartella/cur Esempio/.sottocartella/maildirfolder Esempio/tmp Esempio/cur Esempio/cur/1573461573.M72514P9228Q1.robby-System-Product-Name sottocartella contiene: [] ############################## creato secondo_livello: ['secondo_livello'] Esempio Esempio/new Esempio/.sottocartella Esempio/.sottocartella/new Esempio/.sottocartella/tmp Esempio/.sottocartella/cur Esempio/.sottocartella/.secondo_livello Esempio/.sottocartella/.secondo_livello/new Esempio/.sottocartella/.secondo_livello/tmp Esempio/.sottocartella/.secondo_livello/cur Esempio/.sottocartella/.secondo_livello/maildirfolder Esempio/.sottocartella/maildirfolder Esempio/tmp Esempio/cur Esempio/cur/1573461573.M72514P9228Q1.robby-System-Product-Name ############################## secondo_livello rimosso: [] Esempio Esempio/new Esempio/.sottocartella Esempio/.sottocartella/new Esempio/.sottocartella/tmp Esempio/.sottocartella/cur Esempio/.sottocartella/maildirfolder Esempio/tmp Esempio/cur Esempio/cur/1573461573.M72514P9228Q1.robby-System-Product-Name
Segnalatori di Messaggio
I messaggi nelle caselle di posta hanno segnalatori per tenere traccia di vari aspetti, tipo il fatto che il messaggio sia stato letto oppure no, che un messaggio sia stato contrassegnato come importante o meno, o marcato per una successiva cancellazione. I segnalatori sono conservati come sequenza di specifici codici lettera e le classi Message
hanno metodi per recuperarli e modificarne il valore. Questo esempio mostra i segnalatori per i messaggi nella casella di posta Esempio
prima di aggiungere un segnalatore che indica che quel messaggio è considerato importante.
# mailbox_maildir_add_flag.py
import mailbox
print('Prima:')
mbox = mailbox.Maildir('Esempio')
mbox.lock()
try:
for message_id, message in mbox.iteritems():
print('{:6} "{}"'.format(message.get_flags(),
message['subject']))
message.add_flag('F')
# Si chiede a milabox di aggiornare il messaggio
mbox[message_id] = message
finally:
mbox.flush()
mbox.close()
print('\nDopo:')
mbox = mailbox.Maildir('Esempio')
for message in mbox:
print('{:6} "{}"'.format(message.get_flags(),
message['subject']))
Nella modalità predefinita, i messaggi non hanno segnalatori. L'aggiunta di un segnalatore cambia il messaggio in memoria, ma non aggiorna la versione su disco. Per aggiornare il messaggio su disco si salvi l'oggetto messaggio nella casella di posta usando il suo identificatore.
$ python3 mailbox_maildir_add_flag.py Prima: "Messaggio campione 1" Dopo: F "Messaggio campione 1"
Aggiungere segnalatori con add_flag()
preserva eventuali segnalatori già esistenti. Si utilizzi set_flags()
per sovrascrivere qualsiasi segnalatore esistente, sostituendoli con i nuovi valori passati al metodo.
# mailbox_maildir_set_flags.py
import mailbox
print('Prima:')
mbox = mailbox.Maildir('Esempio')
mbox.lock()
try:
for message_id, message in mbox.iteritems():
print('{:6} "{}"'.format(message.get_flags(),
message['subject']))
message.set_flags('S')
# Si chiede a mailbox di aggiornare il messaggio
mbox[message_id] = message
finally:
mbox.flush()
mbox.close()
print('\nDopo:')
mbox = mailbox.Maildir('Esempio')
for message in mbox:
print('{:6} "{}"'.format(message.get_flags(),
message['subject']))
Il segnalatore F
aggiunto con l'esempio precedente viene perso quando set_flags()
sostituisce i segnalatori con S
in questo esempio.
$ python3 mailbox_maildir_set_flags.py Prima: F "Messaggio campione 1" Dopo: S "Messaggio campione 1"
Altri Formati
mailbox supporta qualche altro formato, ma nessuno di essi è conosciuto come mbox o Maildir. MH è un altro formato di casella di posta multi file, usato da alcuni gestori di mail. Babyl e MMDF sono formati a file singolo con separatori di messaggio diversi da quelli di mbox. I formati a singolo file supportano la stessa API di mbox, e MH include i metodi per la gestione delle cartelle visti nella classe Maildir
.
Vedere anche:
- mailbox
- La documentazione della libreria standard per questo modulo.
- Note di portabilità da Python 2 a 3 per mailbox
- Mbox
- Pagina di wikipedia per il formato Mbox (inglese)
- Maildir
- Pagina di wikipedia per il formato Maildir (inglese)
- Il modulo email
- imaplib
- Il modulo imaplib può lavorare con messaggi email salvati su di un server IMAP