How to Automate Emails with Python and smtplib: Complete Guide
Email is still the backbone of professional communication, and sending emails manually is one of the most time-consuming repetitive tasks developers and businesses face. Python's built-in smtplib module lets you send emails programmatically — from simple notifications to bulk personalized messages — without any external libraries.
In this complete guide, you will learn how to send emails with Python using smtplib, add attachments, create HTML-formatted emails, and build practical email automation scripts. This is an essential skill in your Python automation toolkit.
How Email Works: SMTP Explained Simply
SMTP (Simple Mail Transfer Protocol) is the standard protocol for sending emails across the internet. When you send an email, your email client connects to an SMTP server, authenticates your identity, and hands off the message for delivery. Python's smtplib module handles this entire process for you.
Every major email provider offers SMTP access. Gmail uses smtp.gmail.com on port 587, Outlook uses smtp.office365.com on port 587, and Yahoo uses smtp.mail.yahoo.com on port 587. All require TLS encryption for secure connections.
Setting Up Gmail for Python Email Automation
If you are using Gmail, you need to create an App Password because Google blocks less secure app access by default. Enable 2-Factor Authentication on your Google account, then go to your Google Account security settings and select "App Passwords." Generate a new app password for "Mail" and store it securely in an environment variable.
# Create a .env file
EMAIL_ADDRESS=your.email@gmail.com
EMAIL_PASSWORD=your-16-char-app-password
Sending Your First Email with Python
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from dotenv import load_dotenv
import os
load_dotenv()
def send_email(to_address, subject, body):
sender = os.getenv("EMAIL_ADDRESS")
password = os.getenv("EMAIL_PASSWORD")
msg = MIMEMultipart()
msg["From"] = sender
msg["To"] = to_address
msg["Subject"] = subject
msg.attach(MIMEText(body, "plain"))
try:
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login(sender, password)
server.send_message(msg)
print("Email sent to " + to_address)
except smtplib.SMTPAuthenticationError:
print("Authentication failed. Check credentials.")
except Exception as e:
print("Error: " + str(e))
send_email(
"recipient@example.com",
"Hello from Python",
"This email was sent automatically using Python!"
)
Sending HTML Emails
Plain text emails work for notifications, but HTML emails look more professional. Here is how to send formatted emails:
def send_html_email(to_address, subject, html_content):
sender = os.getenv("EMAIL_ADDRESS")
password = os.getenv("EMAIL_PASSWORD")
msg = MIMEMultipart("alternative")
msg["From"] = sender
msg["To"] = to_address
msg["Subject"] = subject
plain_text = "Your email client does not support HTML."
msg.attach(MIMEText(plain_text, "plain"))
msg.attach(MIMEText(html_content, "html"))
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login(sender, password)
server.send_message(msg)
Adding Email Attachments
Attaching files like PDFs, images, or CSV reports is straightforward with the email.mime modules:
from email.mime.base import MIMEBase
from email import encoders
def send_email_with_attachment(to_address, subject, body, filepath):
sender = os.getenv("EMAIL_ADDRESS")
password = os.getenv("EMAIL_PASSWORD")
msg = MIMEMultipart()
msg["From"] = sender
msg["To"] = to_address
msg["Subject"] = subject
msg.attach(MIMEText(body, "plain"))
filename = os.path.basename(filepath)
with open(filepath, "rb") as f:
attachment = MIMEBase("application", "octet-stream")
attachment.set_payload(f.read())
encoders.encode_base64(attachment)
attachment.add_header(
"Content-Disposition",
"attachment; filename=" + filename
)
msg.attach(attachment)
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login(sender, password)
server.send_message(msg)
print("Email with attachment sent to " + to_address)
Sending Bulk Personalized Emails
One of the most powerful use cases is sending personalized emails to a list of recipients from a CSV file:
import csv
import time
def send_bulk_emails(csv_file, subject_tmpl, body_tmpl):
sender = os.getenv("EMAIL_ADDRESS")
password = os.getenv("EMAIL_PASSWORD")
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login(sender, password)
with open(csv_file, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
sent_count = 0
for row in reader:
name = row["name"]
email = row["email"]
subject = subject_tmpl.format(**row)
body = body_tmpl.format(**row)
msg = MIMEMultipart()
msg["From"] = sender
msg["To"] = email
msg["Subject"] = subject
msg.attach(MIMEText(body, "plain"))
try:
server.send_message(msg)
sent_count += 1
print("Sent to " + name)
time.sleep(2)
except Exception as e:
print("Failed: " + str(e))
print("Bulk send complete: " + str(sent_count) + " emails sent.")
Building a Price Alert Email Bot
Combine email automation with web scraping to build a practical price monitoring system:
import requests
from bs4 import BeautifulSoup
def check_price_and_alert(url, target_price, alert_email):
headers = {"User-Agent": "Mozilla/5.0"}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "html.parser")
price_element = soup.find("span", class_="price")
if not price_element:
print("Could not find price on page")
return
price_text = price_element.get_text(strip=True)
price = float(price_text.replace("$", "").replace(",", ""))
print("Current price: $" + str(price))
if price <= target_price:
send_email(
alert_email,
"Price Alert: $" + str(price),
"The product is now $" + str(price) + ". Target: $" + str(target_price)
)
print("Alert email sent!")
You can schedule this script to run hourly for continuous monitoring.
Email Security Best Practices
Never hardcode credentials. Always use environment variables or a .env file with python-dotenv. Add your .env file to .gitignore to prevent accidentally pushing credentials to GitHub.
Use App Passwords. Never use your main account password. Gmail, Outlook, and other providers offer App Passwords specifically for automated access.
Implement rate limiting. Email providers have sending limits. Gmail allows about 500 emails per day for personal accounts. Add delays between sends to avoid being flagged as spam.
Always use TLS encryption. The starttls() method encrypts the connection between your script and the SMTP server, protecting your credentials and email content.
Combining Email with Other Automation
Email automation becomes incredibly powerful when combined with other scripts. You can automatically send organized file reports from your file organizer, send daily web scraping summaries from your data scrapers, or set up scheduled automated reports that run without any manual intervention.
Conclusion
Python email automation with smtplib eliminates one of the most common time-wasting tasks. From simple notifications to complex bulk email campaigns with attachments, Python gives you full control over your email workflow. Combined with web scraping and task scheduling, you can build powerful monitoring and notification systems that run completely on autopilot.
Continue building your automation skills with our complete Python Automation Guide for Beginners.
تعليقات
إرسال تعليق