Come verificare indirizzi email in Python
Verificare un indirizzo email in Python è semplice per le parti facili e sorprendentemente difficile per la parte che conta di più. Questa guida illustra i controlli di sintassi e le ricerche DNS che puoi eseguire in locale, spiega perché le verifiche SMTP dirette falliscono nella pratica e mostra come ottenere un risultato a livello di casella con un'API REST.
Usa Python per scartare input malformati e, se vuoi, mettere in cache i controlli DNS/MX. Usa un'API di verifica per il risultato a livello di casella, perché le verifiche smtplib dirette vengono facilmente rallentate e spesso restituiscono risposte ambigue.
Cosa significa davvero "verificare" un'email
Verifica email è il processo con cui confermi che un indirizzo è reale e può ricevere posta prima di inviargli qualsiasi cosa. Si scompone in una sequenza di controlli, ognuno più difficile del precedente: l'indirizzo è formattato correttamente, il suo dominio accetta posta, e la casella specifica esiste? In Python i primi due sono facili da fare bene. Il terzo è dove la maggior parte delle soluzioni fatte in casa fallisce in silenzio, e capire perché è la cosa più utile di questo articolo.
Il piano qui sotto è a più livelli. Esegui in locale i controlli economici e veloci per scartare gli input palesemente sbagliati, poi chiama un'API di verifica per sapere, a livello di casella, se l'indirizzo è utilizzabile. Così ottieni il rifiuto immediato degli input spazzatura senza chiamate API e un risultato più chiaro per tutto ciò che supera il filtro.
Passo 1: validazione della sintassi
Il primo controllo conferma che l'indirizzo sia formato correttamente. Si è tentati di scrivere un'espressione regolare, ma la sintassi email è molto più intricata di quanto sembri, e le regex fatte a mano sono quasi sempre troppo rigide o troppo permissive. La libreria di riferimento per questo lavoro è email-validator, che implementa correttamente gli standard pertinenti ed è molto usata in produzione.
Installala con pip:
pip install email-validator
Poi valida un indirizzo. La libreria solleva EmailNotValidError per qualsiasi input malformato e restituisce un oggetto normalizzato per gli input validi:
from email_validator import validate_email, EmailNotValidError
def check_syntax(address):
try:
result = validate_email(address, check_deliverability=False)
return True, result.normalized
except EmailNotValidError as error:
return False, str(error)
ok, value = check_syntax("jane.doe@example.com")
print(ok, value) # True jane.doe@example.com
ok, value = check_syntax("jane.doe@@example")
print(ok, value) # False, with a description of the problem
Impostare check_deliverability=False mantiene questo passo come puro controllo di sintassi senza chiamate di rete, quindi è abbastanza veloce da eseguirlo a ogni battitura o invio di form. Salva sempre il valore normalized restituito dalla libreria invece dell'input grezzo dell'utente, perché canonicalizza l'indirizzo per un'archiviazione coerente.
Passo 2: controlli su dominio e MX
Un indirizzo formattato correttamente resta inutile se il suo dominio non può ricevere email. Il controllo successivo interroga i record DNS del dominio e cerca i record MX, che indicano i server di posta responsabili di quel dominio. Un dominio senza record MX non può accettare posta, quindi qualsiasi indirizzo su quel dominio può essere scartato subito.
Il campo email-validator può farlo per te quando passi check_deliverability=True, che esegue la ricerca MX come parte della validazione:
from email_validator import validate_email, EmailNotValidError
def check_domain(address):
try:
result = validate_email(address, check_deliverability=True)
return True, result.domain
except EmailNotValidError as error:
return False, str(error)
Se vuoi eseguire tu la query DNS, magari per mettere in cache i risultati di molti indirizzi sullo stesso dominio, usa la libreria dnspython direttamente:
pip install dnspython
import dns.exception
import dns.resolver
def has_mx_record(domain):
try:
records = dns.resolver.resolve(domain, "MX")
return len(records) > 0
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
return False
except dns.exception.DNSException:
return False # Treat resolver errors as inconclusive.
print(has_mx_record("gmail.com")) # True
print(has_mx_record("no-such-domain-xyz.invalid")) # False
È davvero utile. Elimina gli indirizzi su domini scaduti, mai esistiti o che semplicemente non gestiscono posta. Ma nota cosa non ti ha ancora detto: se someone@gmail.com è una casella reale. Gmail ha record MX, quindi ogni indirizzo Gmail supera questo controllo, valido o no.
Passo 3: il controllo della casella, e perché l'SMTP diretto non basta
Confermare la casella richiede di parlare con il server di posta ricevente. In teoria ti colleghi via SMTP, ti identifichi, invii un comando RCPT TO con l'indirizzo da verificare e leggi il codice di risposta: un codice positivo suggerisce che la casella esiste, un rifiuto suggerisce di no. Il modulo integrato di Python smtplib può farlo, e il codice sembra ingannevolmente semplice:
import smtplib
import dns.resolver
def naive_smtp_check(address):
domain = address.split("@")[1]
mx_host = str(dns.resolver.resolve(domain, "MX")[0].exchange)
server = smtplib.SMTP(timeout=10)
server.connect(mx_host)
server.helo("example.com")
server.mail("probe@example.com")
code, message = server.rcpt(address)
server.quit()
# 250 and 251 generally mean the mailbox was accepted.
return code in (250, 251)
Funziona in un tutorial e fallisce in produzione. Vale la pena conoscere i motivi nel dettaglio, perché spiegano come mai nessun verificatore serio si affida a uno script del genere.
Reputazione IP e throttling
I provider di posta rilevano attivamente le verifiche automatiche. Esegui una manciata di controlli RCPT TO da un solo server e provider come Gmail e Outlook rallenteranno o bloccheranno quell'IP nel giro di pochi tentativi. Una volta rallentato, il server restituisce risposte ambigue sia che la casella esista sia che no, quindi i tuoi risultati perdono di significato. Proprio per questo la verifica reale si fa da ampi pool di indirizzi IP ben riscaldati e ben gestiti.
Greylisting
Molti server usano il greylisting, restituendo di proposito un errore temporaneo 4xx la prima volta che un mittente sconosciuto li contatta. I server di posta legittimi riprovano più tardi; gli spammer di solito no. Uno script ingenuo che non riprova interpreta quell'errore temporaneo come un rifiuto e segna come non valida una casella perfettamente funzionante. È un falso negativo causato solo dalla fretta.
Domini catch-all
Un dominio catch-all accetta posta per ogni indirizzo possibile, quindi RCPT TO restituisce un codice positivo per realperson@domain.com e nel random-string-9f3a@domain.com allo stesso modo. Un controllo SMTP diretto non riesce a distinguere una casella reale da una inesistente su questi domini. Rilevare un catch-all richiede di inviare una seconda verifica a un indirizzo che sicuramente non esiste e vedere se viene accettato anche quello: è rilevamento catch-all, non un semplice sì o no.
Risposte ambigue dai grandi provider
Alcuni grandi provider hanno reso deliberatamente ambiguo il loro comportamento su RCPT TO . Alcuni accettano ogni indirizzo nella fase di handshake e generano il bounce solo dopo, quindi la risposta SMTP semplicemente non contiene l'informazione che stai cercando.
Ecco perché i maintainer di email-validator lasciano di proposito i controlli SMTP fuori dalla libreria. La loro documentazione dice chiaramente che non c'è nulla da guadagnare a contattare tu stesso un server SMTP, perché i server sono bravi a non rivelare se un indirizzo è consegnabile, e un indirizzo che sembra accettare posta può comunque generare un bounce in seguito. La conclusione onesta: i controlli di sintassi e MX puoi farli bene nel tuo codice, un controllo affidabile della casella no.
Passo 4: verificare con l'API di VeriMails
La soluzione pratica è lasciare che un servizio di verifica gestisca il controllo della casella da un'infrastruttura costruita apposta, e chiamare quel servizio da Python con una singola richiesta HTTP. VeriMails mette a disposizione un' API REST che esegue l'intera pipeline, sintassi, MX, DNS, un handshake SMTP in tempo reale, rilevamento catch-all, rilevamento usa e getta e rilevamento role-based, e restituisce un unico risultato strutturato.
Usando la libreria standard requests , una chiamata di verifica ha questo aspetto. Per i dettagli sugli endpoint, consulta la Documentazione API di VeriMails:
import requests
def verify_email(address, api_key):
response = requests.get(
"https://api.verimails.com/v1/verify",
params={"email": address},
headers={"Authorization": f"Bearer {api_key}"},
timeout=10,
)
response.raise_for_status()
return response.json()
result = verify_email("jane.doe@example.com", api_key="YOUR_API_KEY")
print(result["status"]) # e.g. "deliverable", "undeliverable", "catch_all"
print(result["disposable"]) # True if a throwaway address
print(result["role_based"]) # True for addresses such as info@ or support@
A quel punto puoi agire in base al risultato. Un form di iscrizione potrebbe accettare gli indirizzi deliverable , rifiutare quelli undeliverable e nel disposable e decidere una propria policy per catch_all:
def is_acceptable_for_signup(address, api_key):
result = verify_email(address, api_key)
if result["disposable"]:
return False, "Disposable addresses are not allowed."
if result["status"] == "undeliverable":
return False, "That address does not appear to exist."
return True, "Address verified."
Poiché ogni chiamata restituisce un risultato chiaro, è abbastanza veloce da eseguirla dentro l'invio di un form senza far aspettare l'utente. Per una lista di indirizzi già presenti nel tuo database, la verifica CSV in blocco è la soluzione più adatta, perché caricare un solo file è molto più efficiente che richiamare in loop migliaia di volte l'endpoint per singolo indirizzo.
Lo schema consigliato per un'applicazione Python è combinare i due livelli: mantieni il controllo di sintassi locale con email-validator per scartare subito l'input spazzatura prima di spendere un credito, poi chiama l'API per il risultato di deliverability autorevole su tutto ciò che supera il controllo. Ottieni un feedback rapido per gli errori evidenti e un verdetto affidabile per gli indirizzi reali.
Tabella delle policy applicative
| Risultato | Policy di iscrizione | Policy per import o CRM |
|---|---|---|
deliverable | Accetta e salva l'indirizzo normalizzato | Mantieni nel segmento attivo |
undeliverable | Rifiuta con un messaggio di correzione chiaro | Escludi prima di qualsiasi campagna |
catch_all | Accetta o rivedi in base al rischio dell'account | Segmenta a parte e invia con prudenza |
disposable | Rifiuta per la maggior parte dei prodotti | Escludi dalle liste di outreach |
role_based | Consenti solo se le inbox condivise sono accettabili | Rivedi prima della cold outreach |
Chiamate API e job su liste a confronto
Usa l'API dove serve una decisione immediata: iscrizione, checkout, form per i lead, inviti e modifiche all'account. Usa la verifica in blocco per un export CSV, una pulizia del CRM o il pubblico di una campagna, dove migliaia di indirizzi richiedono la stessa revisione. Entrambi i flussi dovrebbero alimentare le stesse regole di esclusione, così un indirizzo rifiutato in un percorso non riceve email più avanti da un altro strumento.
Mettere tutto insieme
Un'implementazione pulita ha tre livelli. Primo, un controllo di sintassi email-validator con check_deliverability=False, eseguito in locale e all'istante per scartare gli input malformati. Secondo, un controllo MX facoltativo con dnspython se vuoi filtrare i domini morti prima di chiamare l'API, anche se l'API copre già questo aspetto. Terzo, una chiamata all'API di VeriMails per il risultato a livello di casella, compreso il rilevamento di catch-all, usa e getta e role-based che non puoi riprodurre in modo affidabile da solo.
La cosa da ricordare è la divisione dei compiti. Sintassi e DNS puoi gestirli davvero tu in Python. Il controllo della casella in tempo reale dipende da reputazione IP, comportamento del greylisting e rilevamento catch-all, che un singolo script non può gestire, ed è per questo che la libreria di validazione standard lascia di proposito fuori le verifiche SMTP. Usa i controlli locali per la velocità, un'API per la verifica a livello di casella e la pagina dei prezzi per scegliere il piano adatto al volume che prevedi.
Domande frequenti
Articoli correlati
Parti da dati puliti
100 crediti gratuiti all'iscrizione. Nessuna carta di credito richiesta. Metti in pratica questi consigli oggi stesso.
Inizia Gratis