⟳ Automazione

Salvare in automatico gli allegati email con Python e IMAP

Come scaricare in automatico gli allegati email con Python e IMAP: cercare i messaggi, salvare i file con nomi sicuri e segnare quelli elaborati.

Fatture dei fornitori, conferme d'ordine, rendiconti mensili, scansioni inviate dalla stampante multifunzione: molti documenti arrivano in ufficio come allegati email. Aprire ogni messaggio, scaricare il file e spostarlo nella cartella giusta è un'attività che si ripete decine di volte alla settimana. Con il protocollo IMAP e la libreria standard di Python si può affidare tutto a uno script che legge la casella, salva gli allegati e tiene traccia di ciò che ha già fatto.

IMAP in breve

IMAP è il protocollo con cui i client di posta leggono i messaggi lasciandoli sul server. Uno script può usarlo esattamente come Outlook o Thunderbird: si collega, sceglie una cartella, cerca messaggi secondo criteri e li scarica. Servono l'indirizzo del server IMAP, di norma con cifratura sulla porta 993, e le credenziali. Per gli account con verifica in due passaggi si genera una password per app, come per l'invio via SMTP; alcuni servizi aziendali richiedono invece un'autenticazione basata su token, da configurare secondo la documentazione del provider.

Collegarsi e cercare i messaggi

import imaplib

with imaplib.IMAP4_SSL("imap.provider.it") as imap:
    imap.login(utente, password)
    imap.select("INBOX")
    stato, dati = imap.uid("search", None, '(UNSEEN FROM "fatture@fornitore.it")')
    uid_trovati = dati[0].split()
    print(len(uid_trovati), "messaggi da elaborare")

Il criterio di ricerca segue la sintassi IMAP. Alcuni esempi utili:

Criterio Significato
UNSEEN Messaggi non letti
FROM "indirizzo" Mittente specifico
SUBJECT "fattura" Parola nell'oggetto
SINCE 01-Mar-2025 Ricevuti da quella data in poi
(UNSEEN SUBJECT "ordine") Più condizioni insieme

Il formato della data usa il mese abbreviato in inglese, indipendentemente dalla lingua dell'account. Conviene usare gli identificativi UID, tramite imap.uid(...), invece dei numeri di sequenza: i numeri cambiano quando si spostano o cancellano messaggi, gli UID restano stabili.

Estrarre gli allegati

Ogni messaggio si scarica in formato grezzo e si interpreta con il modulo email, che sa scomporre le parti di un messaggio complesso:

import email
from email import policy
from pathlib import Path

destinazione = Path("C:/Amministrazione/Fatture in arrivo")

for uid in uid_trovati:
    stato, parti = imap.uid("fetch", uid, "(RFC822)")
    messaggio = email.message_from_bytes(parti[0][1], policy=policy.default)
    for allegato in messaggio.iter_attachments():
        nome = allegato.get_filename()
        if not nome or not nome.lower().endswith(".pdf"):
            continue
        percorso = destinazione / nome_sicuro(nome)
        percorso.write_bytes(allegato.get_content())

Con policy=policy.default il modulo restituisce oggetti moderni, decodifica automaticamente i nomi dei file con caratteri accentati e offre iter_attachments(), che elenca solo le parti marcate come allegati.

Nomi di file sicuri

Il nome di un allegato è scelto da chi invia il messaggio e non va mai usato così com'è. Può contenere caratteri non validi su Windows, essere identico a quello di un file già salvato o, in casi malevoli, includere sequenze come ..\ per scrivere fuori dalla cartella prevista. Una funzione di pulizia risolve tutti e tre i problemi:

import re
from datetime import date

def nome_sicuro(nome):
    nome = Path(nome).name                      # elimina eventuali percorsi
    nome = re.sub(r'[\\/:*?"<>|]', "_", nome).strip(" .")
    return f"{date.today():%Y-%m-%d}_{nome}"

Anteporre la data aiuta l'ordinamento e riduce le collisioni. Per eliminarle del tutto, prima di salvare si controlla se il file esiste e, in quel caso, si aggiunge un suffisso numerico.

Segnare i messaggi elaborati

Lo script deve ricordare quali messaggi ha già trattato, altrimenti alla prossima esecuzione salverà gli stessi allegati. Le strategie sono tre:

  1. segnare come letto: il fetch con RFC822 lo fa già automaticamente su molti server; è semplice, ma se una persona apre il messaggio prima dello script, questo non lo vedrà più;
  2. spostare in una cartella dedicata, per esempio «Elaborati»: è la scelta più chiara, perché la casella mostra a colpo d'occhio cosa resta da fare. Si fa con imap.uid("copy", uid, "Elaborati") seguito dalla marcatura del messaggio come eliminato; molti server supportano anche il comando MOVE in un solo passaggio;
  3. registrare gli UID in un file locale: indipendente dallo stato dei messaggi, ma da mantenere con cura.

Nel mio lavoro preferisco la seconda: lascia una traccia visibile anche a chi non conosce lo script e permette di rielaborare un messaggio semplicemente riportandolo nella posta in arrivo.

Sicurezza degli allegati

Salvare automaticamente file ricevuti da Internet richiede prudenza. Limitate lo script ai mittenti attesi e ai tipi di file previsti, come i PDF, ignorando eseguibili, archivi compressi e documenti con macro. Ricordate inoltre che il mittente di un'email può essere falsificato: filtrare per indirizzo riduce il rumore, ma non garantisce l'autenticità. I file salvati non vanno mai aperti automaticamente; vanno messi a disposizione delle persone, che li tratteranno come qualsiasi altro allegato, con l'antivirus aziendale attivo sulla cartella di destinazione.

In sintesi

  • imaplib permette di cercare e scaricare messaggi; usate gli UID e la connessione cifrata.
  • Il modulo email con policy.default estrae gli allegati e ne decodifica i nomi.
  • Ripulite sempre i nomi dei file e anteponete la data per evitare collisioni.
  • Spostate i messaggi elaborati in una cartella dedicata per non trattarli due volte.
  • Filtrate mittenti e tipi di file e non aprite mai automaticamente gli allegati.
MM
Marco Marzaduri

Marco Marzaduri si occupa di cybersecurity, informatica forense e sviluppo web. Laureato in informatica, unisce il rigore dell'analisi tecnica all'attenzione per le persone che usano la tecnologia.

Leggi anche