【Python】Sending Emails with smtplib and EmailMessage: Supporting HTML Emails

目次

Overview

This article explains how to send emails using Python’s standard libraries: smtplib and email.message. We will use the modern EmailMessage class, which is recommended for Python 3.6 and later, instead of the older and more complex MIMEText. We cover both plain text and HTML formats.

Specifications (Input/Output)

  • Input:
    • Sender address (From)
    • Recipient address (To)
    • SMTP server settings (Host, Port, Credentials)
    • Email subject and body (Text or HTML)
  • Output:
    • Execution of email sending via SMTP server
    • Success or failure logs
  • Requirement: Access to an SMTP server (such as Gmail or a local test server).

Basic Usage

This is a simple example of sending a plain text email. Using the with statement ensures that the SMTP connection is closed automatically.

import smtplib
from email.message import EmailMessage

# Create the message
msg = EmailMessage()
msg["Subject"] = "Hello Python"
msg["From"] = "sender@example.com"
msg["To"] = "receiver@example.com"
msg.set_content("This is a test email sent from Python.")

# Sending the email (Change settings to match your environment)
# This example assumes a local test server at localhost:1025
with smtplib.SMTP("localhost", 1025) as smtp:
    smtp.send_message(msg)

Full Code

This is a practical function that sends an HTML email. It also includes a plain text version for email clients that do not support HTML. This example uses Gmail’s SMTP server.

import smtplib
from email.message import EmailMessage
import ssl

def send_html_email():
    """
    Demo function to send an HTML email.
    Sets plain text as a fallback for non-HTML environments.
    """
    # 1. Configuration (Recommended to use environment variables in production)
    smtp_host = "smtp.gmail.com"
    smtp_port = 587
    my_email = "your_email@gmail.com"
    my_password = "your_app_password" # Use an App Password for Gmail
    
    to_email = "target_address@example.com"

    # 2. Create EmailMessage object and set headers
    msg = EmailMessage()
    msg["Subject"] = "[Notification] HTML Email from Python"
    msg["From"] = my_email
    msg["To"] = to_email

    # 3. Set the body (Multipart structure)
    # First, set the plain text content for non-HTML environments
    text_content = "Hello.\nThis is an HTML email.\nPlease view it in a compatible environment."
    msg.set_content(text_content)

    # Next, add the HTML body using add_alternative
    # This ensures the HTML version is prioritized by the recipient's client
    html_content = """
    <html>
      <body>
        <h1 style="color: #2E86C1;">HTML Email Test</h1>
        <p>This email uses Python's <b>EmailMessage</b> class.</p>
        <p>Link: <a href="https://www.python.org">Python.org</a></p>
      </body>
    </html>
    """
    msg.add_alternative(html_content, subtype="html")

    # 4. Connect to SMTP server and send
    try:
        print("Connecting to SMTP server...")
        
        with smtplib.SMTP(smtp_host, smtp_port) as smtp:
            # Enable debug levels if you want to see the server communication
            # smtp.set_debuglevel(1)
            
            # Start encryption
            context = ssl.create_default_context()
            smtp.starttls(context=context)
            
            # Login
            smtp.login(my_email, my_password)
            
            # Send
            smtp.send_message(msg)
            print("Email sent successfully.")

    except smtplib.SMTPAuthenticationError:
        print("Authentication Error: Incorrect email address or password.")
    except Exception as e:
        print(f"An error occurred: {e}")

if __name__ == "__main__":
    send_html_email()

Customization Points

Creating and Operating EmailMessage Objects

These are the main methods of the email.message module.

SyntaxMeaning / Process
msg = EmailMessage()Creates a new empty email message object. You can set headers (Subject, From, To) like a dictionary.
msg.set_content(body)Sets the main body of the email. By default, it is treated as plain text (text/plain).
msg.add_alternative(body, subtype="html")Adds an alternative version (like HTML) as a multipart message. If called after set_content, HTML-compatible clients will display this version.

Creating and Operating smtplib.SMTP Objects

These are the main methods of the smtplib module.

SyntaxMeaning / Process
smtplib.SMTP(HOST, PORT)Creates a connection object to the SMTP server. Usually, port 587 (STARTTLS) or 25 (non-encrypted/local) is used.
smtp.starttls()Switches the connection to TLS (Transport Layer Security) mode to encrypt the communication path. Must be executed before sending credentials.
smtp.login(EMAIL, PASS)Sends authentication credentials to the SMTP server. For Gmail, you may need an “App Password” instead of your regular password.
smtp.send_message(msg)Analyzes the EmailMessage object and sends the email in the correct format.

Important Notes

Security and Password Management

It is dangerous to write passwords directly in your script. You should read them from environment variables (os.environ) or external configuration files. When using Gmail, you must enable two-factor authentication and generate an “App Password.” Regular login passwords will be rejected.

Port Numbers

  • 587: Used with smtplib.SMTP("host", 587) followed by starttls(). This is the recommended method.
  • 465: Used with smtplib.SMTP_SSL("host", 465) to start an SSL connection immediately.

Sending Limits

Free SMTP servers often have limits on the number of emails you can send per day and use spam filters. These are not suitable for sending large volumes of emails, such as newsletters.

Advanced Usage

Adding attachments is easy with EmailMessage.

import mimetypes

# (Code for creating msg is omitted)

# File you want to attach
file_path = "report.pdf"

with open(file_path, "rb") as f:
    file_data = f.read()
    file_name = f.name

# Automatically determine the MIME type
guessed_type = mimetypes.guess_type(file_name)[0]
if guessed_type:
    maintype, subtype = guessed_type.split("/")
else:
    maintype, subtype = "application", "octet-stream"

# Add attachment
msg.add_attachment(
    file_data,
    maintype=maintype,
    subtype=subtype,
    filename=file_name
)

Summary

Sending emails in Python has become very intuitive with the EmailMessage class. You can use set_content for text only and combine it with add_alternative for HTML emails. Always use the with statement and try-except blocks for proper connection management and error handling.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次