Envoyer des emails en Python est simple grâce aux modules natifs smtplib et email. Que ce soit pour des notifications automatiques, des rapports ou des alertes, Python offre une API complète pour gérer l’envoi d’emails (texte, HTML, pièces jointes).
Dans ce tutoriel, vous découvrirez :
- Comment envoyer un email simple avec
smtplib - Configurer Gmail, Outlook, et serveurs SMTP personnalisés
- Envoyer des emails HTML avec mise en forme
- Ajouter des pièces jointes (PDF, images, fichiers)
- Gérer les erreurs et les bonnes pratiques de sécurité
- Intégration avec Flask (Flask-Mail)
1) Installation et prérequis
Les modules smtplib et email sont natifs en Python (aucune installation requise).
python --version
# Python 3.7+ recommandé
2) Envoyer un email simple (texte brut)
Code minimal
import smtplib
from email.message import EmailMessage
# Créer le message
msg = EmailMessage()
msg['Subject'] = 'Test depuis Python'
msg['From'] = 'votre.email@gmail.com'
msg['To'] = 'destinataire@example.com'
msg.set_content('Ceci est un email de test envoyé depuis Python.')
# Envoyer via SMTP
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls() # Connexion sécurisée
smtp.login('votre.email@gmail.com', 'votre_mot_de_passe')
smtp.send_message(msg)
print("Email envoyé avec succès !")
Explication :
EmailMessage(): crée un message emailmsg['Subject'],msg['From'],msg['To']: en-têtesset_content(): corps du message en texte brutSMTP('smtp.gmail.com', 587): serveur Gmail sur le port 587 (STARTTLS)starttls(): upgrade vers une connexion chiffrée TLSlogin(): authentificationsend_message(): envoi du message
3) Configuration des serveurs SMTP populaires
L’exemple precedent utilise Gmail, mais smtplib fonctionne avec n’importe quel fournisseur SMTP. Voici les paramètres des plus courants.
Gmail
Prérequis : activer les “Mots de passe d’application” (App Passwords)
- Aller dans Compte Google > Sécurité
- Activer la validation en deux étapes
- Générer un “Mot de passe d’application”
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
EMAIL = 'votre.email@gmail.com'
PASSWORD = 'abcd efgh ijkl mnop' # Mot de passe d'application
Outlook / Hotmail
SMTP_SERVER = 'smtp-mail.outlook.com'
SMTP_PORT = 587
EMAIL = 'votre.email@outlook.com'
PASSWORD = 'votre_mot_de_passe'
Office 365
SMTP_SERVER = 'smtp.office365.com'
SMTP_PORT = 587
EMAIL = 'votre.email@entreprise.com'
PASSWORD = 'votre_mot_de_passe'
Yahoo Mail
SMTP_SERVER = 'smtp.mail.yahoo.com'
SMTP_PORT = 587
EMAIL = 'votre.email@yahoo.com'
PASSWORD = 'mot_de_passe_application' # Générer sur Yahoo
Serveur SMTP personnalisé
SMTP_SERVER = 'mail.mondomaine.com'
SMTP_PORT = 587 # ou 465 pour SSL direct
EMAIL = 'contact@mondomaine.com'
PASSWORD = 'mot_de_passe'
4) Envoyer un email HTML
Avec mise en forme
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Rapport mensuel'
msg['From'] = 'votre.email@gmail.com'
msg['To'] = 'destinataire@example.com'
# Contenu HTML
html_content = """
<html>
<head></head>
<body>
<h1 style="color: #2e6c80;">Rapport du mois</h1>
<p>Bonjour,</p>
<p>Voici le <strong>rapport mensuel</strong> :</p>
<ul>
<li>Ventes : +15%</li>
<li>Utilisateurs : 1 250</li>
<li>Revenus : 50 000€</li>
</ul>
<p>Cordialement,<br>L'équipe</p>
</body>
</html>
"""
msg.set_content('Version texte brut (fallback)') # Fallback pour clients sans HTML
msg.add_alternative(html_content, subtype='html')
# Envoi
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login('votre.email@gmail.com', 'votre_mot_de_passe')
smtp.send_message(msg)
print("Email HTML envoyé !")
Points clés :
set_content(): version texte (fallback)add_alternative(..., subtype='html'): version HTML- Les clients email afficheront le HTML, sinon le texte brut
5) Envoyer à plusieurs destinataires
Destinataires multiples (To, Cc, Bcc)
msg = EmailMessage()
msg['Subject'] = 'Réunion d\'équipe'
msg['From'] = 'votre.email@gmail.com'
msg['To'] = 'alice@example.com, bob@example.com' # Liste séparée par virgules
msg['Cc'] = 'manager@example.com' # Copie
msg['Bcc'] = 'archive@example.com' # Copie cachée
msg.set_content('Rappel : réunion demain à 10h.')
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login('votre.email@gmail.com', 'votre_mot_de_passe')
smtp.send_message(msg)
Alternative avec une liste :
destinataires = ['alice@example.com', 'bob@example.com', 'charlie@example.com']
msg['To'] = ', '.join(destinataires)
6) Ajouter des pièces jointes
Fichier texte, PDF, image
import smtplib
from email.message import EmailMessage
from pathlib import Path
msg = EmailMessage()
msg['Subject'] = 'Document joint'
msg['From'] = 'votre.email@gmail.com'
msg['To'] = 'destinataire@example.com'
msg.set_content('Veuillez trouver ci-joint le document.')
# Ajouter une pièce jointe
file_path = Path('rapport.pdf')
with open(file_path, 'rb') as f:
file_data = f.read()
file_name = file_path.name
msg.add_attachment(file_data, maintype='application', subtype='pdf', filename=file_name)
# Envoi
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login('votre.email@gmail.com', 'votre_mot_de_passe')
smtp.send_message(msg)
print(f"Email avec {file_name} envoyé !")
Plusieurs pièces jointes
fichiers = ['rapport.pdf', 'graphique.png', 'data.csv']
for fichier in fichiers:
with open(fichier, 'rb') as f:
file_data = f.read()
file_name = Path(fichier).name
# Détection automatique du type MIME
import mimetypes
mime_type, _ = mimetypes.guess_type(fichier)
maintype, subtype = mime_type.split('/') if mime_type else ('application', 'octet-stream')
msg.add_attachment(file_data, maintype=maintype, subtype=subtype, filename=file_name)
Image inline (intégrée dans le HTML)
msg = EmailMessage()
msg['Subject'] = 'Newsletter'
msg['From'] = 'votre.email@gmail.com'
msg['To'] = 'destinataire@example.com'
html = """
<html>
<body>
<h1>Nouvelle version disponible !</h1>
<img src="cid:logo">
</body>
</html>
"""
msg.add_alternative(html, subtype='html')
# Ajouter l'image avec un Content-ID
with open('logo.png', 'rb') as img:
msg.get_payload()[0].add_related(img.read(), maintype='image', subtype='png', cid='<logo>')
# Envoi...
7) Gestion des erreurs
Erreurs courantes
import smtplib
from email.message import EmailMessage
try:
msg = EmailMessage()
msg['Subject'] = 'Test'
msg['From'] = 'votre.email@gmail.com'
msg['To'] = 'destinataire@example.com'
msg.set_content('Test')
with smtplib.SMTP('smtp.gmail.com', 587, timeout=10) as smtp:
smtp.starttls()
smtp.login('votre.email@gmail.com', 'votre_mot_de_passe')
smtp.send_message(msg)
print("✅ Email envoyé avec succès")
except smtplib.SMTPAuthenticationError:
print("❌ Erreur d'authentification (email/mot de passe incorrect)")
except smtplib.SMTPException as e:
print(f"❌ Erreur SMTP : {e}")
except Exception as e:
print(f"❌ Erreur : {e}")
Erreurs fréquentes :
SMTPAuthenticationError: identifiants incorrectsSMTPRecipientsRefused: email destinataire invalideSMTPServerDisconnected: connexion perduesocket.gaierror: serveur SMTP introuvable
8) Utiliser SSL (port 465) au lieu de STARTTLS
Tous les exemples precedents utilisent starttls() sur le port 587. Certains serveurs supportent aussi une connexion SSL directe sur le port 465 via SMTP_SSL.
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Test SSL'
msg['From'] = 'votre.email@gmail.com'
msg['To'] = 'destinataire@example.com'
msg.set_content('Test avec SSL')
# SMTP_SSL sur le port 465
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login('votre.email@gmail.com', 'votre_mot_de_passe')
smtp.send_message(msg)
print("Email envoyé via SSL")
Différence :
- Port 587 + STARTTLS : connexion non chiffrée upgradée vers TLS (recommandé)
- Port 465 + SSL : connexion chiffrée dès le début (ancien standard, toujours supporté)
9) Sécurité : ne pas hardcoder les mots de passe
Les exemples precedents utilisent des mots de passe en clair pour rester lisibles. En production, il faut externaliser ces secrets dans des variables d’environnement.
import os
from dotenv import load_dotenv
# Charger depuis .env
load_dotenv()
EMAIL = os.getenv('EMAIL')
PASSWORD = os.getenv('EMAIL_PASSWORD')
# Utilisation
smtp.login(EMAIL, PASSWORD)
Fichier .env :
EMAIL=votre.email@gmail.com
EMAIL_PASSWORD=abcd efgh ijkl mnop
Installation :
pip install python-dotenv
⚠️ Important : ajoutez .env à votre .gitignore pour ne pas committer vos secrets.
10) Intégration avec Flask (Flask-Mail)
Pour envoyer des emails dans une application web Flask.
Installation
pip install Flask-Mail
Configuration
from flask import Flask
from flask_mail import Mail, Message
app = Flask(__name__)
# Configuration SMTP
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = 'votre.email@gmail.com'
app.config['MAIL_PASSWORD'] = 'votre_mot_de_passe'
app.config['MAIL_DEFAULT_SENDER'] = 'votre.email@gmail.com'
mail = Mail(app)
@app.route('/send')
def send_email():
msg = Message('Hello from Flask', recipients=['destinataire@example.com'])
msg.body = 'Ceci est un email envoyé depuis Flask.'
msg.html = '<h1>Hello</h1><p>Email HTML depuis Flask.</p>'
mail.send(msg)
return 'Email envoyé !'
if __name__ == '__main__':
app.run(debug=True)
11) Envoi asynchrone avec threading
Pour ne pas bloquer l’exécution lors de l’envoi d’emails.
import smtplib
from email.message import EmailMessage
import threading
def envoyer_email_async(destinataire, sujet, contenu):
def _send():
msg = EmailMessage()
msg['Subject'] = sujet
msg['From'] = 'votre.email@gmail.com'
msg['To'] = destinataire
msg.set_content(contenu)
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login('votre.email@gmail.com', 'votre_mot_de_passe')
smtp.send_message(msg)
print(f"Email envoyé à {destinataire}")
thread = threading.Thread(target=_send)
thread.start()
# Utilisation
envoyer_email_async('destinataire@example.com', 'Test async', 'Message de test')
print("L'envoi est en cours en arrière-plan...")
Alternative avec asyncio (Python 3.7+) :
import asyncio
import smtplib
from email.message import EmailMessage
async def envoyer_email_async(destinataire, sujet, contenu):
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, envoyer_email_sync, destinataire, sujet, contenu)
def envoyer_email_sync(destinataire, sujet, contenu):
msg = EmailMessage()
msg['Subject'] = sujet
msg['From'] = 'votre.email@gmail.com'
msg['To'] = destinataire
msg.set_content(contenu)
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login('votre.email@gmail.com', 'votre_mot_de_passe')
smtp.send_message(msg)
# Utilisation
asyncio.run(envoyer_email_async('destinataire@example.com', 'Test', 'Message'))
12) Templates d’emails avec Jinja2
Plutôt que de construire le HTML a la main comme dans la section 4, Jinja2 permet de separer le template des donnees.
Installation
pip install Jinja2
Template HTML (email_template.html)
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
h1 { color: #2e6c80; }
</style>
</head>
<body>
<h1>Bonjour !</h1>
<p>Vous avez nouvelles notifications.</p>
<ul>
</ul>
<p>Cordialement,<br>L'équipe</p>
</body>
</html>
Code Python
from jinja2 import Template
import smtplib
from email.message import EmailMessage
# Charger le template
with open('email_template.html', 'r', encoding='utf-8') as f:
template = Template(f.read())
# Rendre le template avec des données
html_content = template.render(
nom='Alice',
nb_notifications=3,
notifications=['Nouveau message', 'Commentaire sur votre post', 'Mise à jour système']
)
# Créer et envoyer l'email
msg = EmailMessage()
msg['Subject'] = 'Vos notifications'
msg['From'] = 'votre.email@gmail.com'
msg['To'] = 'alice@example.com'
msg.add_alternative(html_content, subtype='html')
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login('votre.email@gmail.com', 'votre_mot_de_passe')
smtp.send_message(msg)
print("Email avec template envoyé !")
13) Bonnes pratiques
✅ À faire
- Utiliser des mots de passe d’application (Gmail, Yahoo) plutôt que le mot de passe principal
- Stocker les credentials dans des variables d’environnement (
.env) - Gérer les erreurs avec des try/except appropriés
- Ajouter un timeout :
SMTP(..., timeout=10) - Utiliser STARTTLS ou SSL pour chiffrer les connexions
- Valider les adresses email avant l’envoi (regex ou lib
email-validator) - Limiter le taux d’envoi pour éviter d’être banni (rate limiting)
- Respecter le RGPD : permettre le désabonnement
❌ À éviter
- Hardcoder les mots de passe dans le code
- Envoyer des emails en masse sans throttling
- Ne pas gérer les exceptions
- Envoyer des emails non sécurisés (sans TLS/SSL)
- Oublier de fermer la connexion SMTP (utilisez
with)
14) Cas d’usage pratiques
Voici quelques exemples concrets qui combinent les techniques vues dans cet article.
1. Notification d’erreur
def notifier_erreur(exception):
msg = EmailMessage()
msg['Subject'] = f'[ERREUR] Application crash : {type(exception).__name__}'
msg['From'] = 'monitoring@monapp.com'
msg['To'] = 'admin@monapp.com'
msg.set_content(f"Une erreur s'est produite :\n\n{str(exception)}")
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login('monitoring@monapp.com', 'password')
smtp.send_message(msg)
2. Rapport quotidien automatisé
import schedule
import time
def envoyer_rapport_quotidien():
# Générer le rapport
rapport = generer_rapport()
msg = EmailMessage()
msg['Subject'] = f'Rapport quotidien - {date.today()}'
msg['From'] = 'rapport@monapp.com'
msg['To'] = 'direction@monapp.com'
msg.set_content(rapport)
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login('rapport@monapp.com', 'password')
smtp.send_message(msg)
print("Rapport envoyé")
# Programmer l'envoi tous les jours à 8h
schedule.every().day.at("08:00").do(envoyer_rapport_quotidien)
while True:
schedule.run_pending()
time.sleep(60)
3. Email de confirmation d’inscription
def envoyer_confirmation_inscription(email_utilisateur, token):
lien_confirmation = f"https://monapp.com/confirm?token={token}"
html = f"""
<html>
<body>
<h1>Bienvenue !</h1>
<p>Merci pour votre inscription.</p>
<p><a href="{lien_confirmation}">Confirmez votre email</a></p>
</body>
</html>
"""
msg = EmailMessage()
msg['Subject'] = 'Confirmez votre inscription'
msg['From'] = 'noreply@monapp.com'
msg['To'] = email_utilisateur
msg.add_alternative(html, subtype='html')
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
smtp.starttls()
smtp.login('noreply@monapp.com', 'password')
smtp.send_message(msg)
Conclusion
Avec smtplib et email, Python couvre la plupart des besoins d’envoi d’emails sans dépendance externe. Pour une integration web, Flask-Mail simplifie encore les choses.
Points clés à retenir :
smtplib+email: modules natifs, pas d’installation- Configuration SMTP : Gmail (587/465), Outlook, serveurs personnalisés
- HTML + pièces jointes :
add_alternative()+add_attachment() - Sécurité : variables d’environnement, mots de passe d’application
- Gestion d’erreurs avec try/except
- Flask-Mail : intégration web
- Templates dynamiques avec Jinja2
Envoyez vos premiers emails automatisés dès maintenant ! 📧