Sending Emails With Python is one of my blog posts

You may have found this tutorial because you want to use Python to send email. Perhaps you want to write code to receive mail reminders, send confirmation emails to users when they create an account, or send emails to organization members to remind them to pay dues. Sending mail is a time-consuming and error-prone task, but it can be easily automated using Python.

In this tutorial, you will learn how to:

  • Use SMTP_SSL() and.starttls() to set up a secure connection

  • Use Python’s built-in smtplib library to send basic email

  • Use an email package to send an email containing HTML content and attachments

  • Send multiple personalized emails containing CSV files of contact data

  • Using the Yagmail package requires just a few lines of code to send emails from your Gmail account

You’ll find transactional E-mail services at the end of this tutorial, which can be useful when you want to send a lot of E-mail.

Ready to start

Python has the smtPLib module built in for sending emails using the Simple Mail Transfer Protocol (SMTP). Smtplib uses THE RFC 821 protocol in SMTP. The examples in this tutorial will use the Gmail SMTP server to send email, but the same principles apply to other email services. While most email providers use the same connection ports as in this tutorial, you can use a Google search to quickly confirm.

To start this tutorial, set up a Gmail account for development, or set up an SMTP debug server where messages will not be sent but printed to the console. These two options are listed below. The local SMTP debug server can be used to fix any problems with the email function and ensure that your email function is bug free before sending any email.

Option 1: Set up a Gmail account for development

If you decide to use a Gmail account to send emails, I strongly recommend setting up a one-time account for development code. This is because you have to adjust the security Settings of your Gmail account to allow access from your Python code, and you may also accidentally reveal your login details. In addition, I found that my test account’s inbox filled up quickly with test emails, which is also enough for you to set up a new Gmail account for development.

One nice feature of Gmail is that you can use the + sign to add any modifier to your email address before the @ sign. For example, messages sent to [email protected] and [email protected] will be sent to [email protected]. When testing E-mail functionality, you can use this feature to simulate all multiple addresses pointing to the same inbox.

To create a Gmail address to test your code, do the following:

  • Create a new Google account
  • Enable to allow insecure applications. Please note that this makes it easier for others to access your account.

If you don’t want to degrade the security Settings of your Gmail account, check out Google’s documentation on how to use a Python script for OAuth2 authorization to get access credentials.

Option 2: Create a local SMTP server

You can test email functionality by running a local SMTP debug server using Python’s pre-installed SMPTD module. Instead of sending e-mails to the specified address, it drops them and prints their contents to the console. Running the local debug server means you don’t have to deal with message encryption or log in to the E-mail server with credentials.

You can start the local SMTP debug server by typing the following at the command prompt:

python -m smtpd -c DebuggingServer -n localhost:1025
Copy the code

On Linux, sudo precedes the same command.

Any E-mail sent through this server is discarded and a bytes object per line is displayed in the terminal window:

---------- MESSAGE FOLLOWS ----------
b'X-Peer: ::1'
b''
b'From: [email protected]'
b'To: [email protected]'
b'Subject: a local test mail'
b''
b'Hello there, here is a test email'
------------ END MESSAGE ------------
Copy the code

For the rest of this tutorial, I’m assuming you’re using a Gmail account, but if you’re using a local debug server, make sure to use localhost as your SMTP server address and use port 1025 instead of port 465 or 587. Otherwise, you don’t need to use login() or use SSL/TLS to encrypt communications.


Send plain text messages

Before we dive into sending emails with HTML content and attachments, you’ll learn to send plain text emails in Python. These are emails that you can write in a simple text editor. There are no fancy things like text formatting or hyperlinks. You’ll learn more about that later.

Start a secure SMTP connection

When you send email through Python, make sure your SMTP connection is encrypted so that others cannot easily access your mail and login credentials. SSL (Secure Socket Layer) and TLS (Transport Layer Security) are two protocols that can be used to encrypt SMTP connections. You do not need to use either of these when using a local debug server.

There are two ways to establish a secure connection with your email server:

  • useSMTP_SSL()Establish a secure SMTP connection
  • Use an insecure SMTP connection, then use.starttls()encryption

In both cases, Gmail uses TLS to encrypt emails because it’s a more secure successor to SSL. Given Python’s security considerations, it is strongly recommended that you use create_default_context() in the SSL module. This will load the system’s trusted CA certificate, enable hostname checking and certificate validation, and try to select the appropriate secure protocol and password Settings.

To check the encrypted messages for emails in your Gmail inbox, go to More → Show Raw Content to see the encryption types listed under the received heading.

Smtplib is a built-in module in Python for sending E-mail to any Internet computer using SMTP or ESMTP listener daemons.

I’ll show you how to use SMTP_SSL() first, because it instantiates a secure connection from the start and is slightly cleaner than using the.starttls() alternative. Note that Gmail requires you to connect to port 465 if you use SMTP_SSL() and 589 if you use.starttls().

Option 1: UseSMPT_SSL()

The following code example uses smTPLib’s SMTP_SSL() to initiate a TLS encrypted connection to establish a secure connection with Gmail’s SMTP server. The default context for SSL validates the host name and its certificate and optimizes the security of the connection. Please be sure to fill in your own email address instead of [email protected]:

import smtplib, ssl

port = 465  # For SSL
password = input("Type your password and press enter: ")

# Create a secure SSL context
context = ssl.create_default_context()

with smtplib.SMTP_SSL("smtp.gmail.com", port, context=context) as server:
    server.login("[email protected]", password)
    # TODO: Send email here
Copy the code

Use with smtplib.smtp_ssl () as server to ensure that the connection closes automatically at the end of the indentation block. If port is zero or not specified, the standard port (port 465) is used by.smtp_ssl ().

Storing email passwords in code is not secure, especially if you plan to share them with others. Instead, use input() to let the user enter the password when running the script, as shown in the example above. If you don’t want the password to appear on the screen as you type, you can import the getPass module and use.getPass () instead of entering the password directly.

Option 2: Use.starttls()

Instead of using.smtp_ssl () to create a secure connection in the first place, we can create an insecure SMTP connection and encrypt it with.starttls().

To do this, create an instance of smtplib.smtp that encapsulates the SMTP connection and allows you access to its methods. I recommend that you define your SMTP server and port at the beginning of your script so that you can easily configure them.

The following code snippet uses server= SMTP() instead of with SMTP() as server: the format we used in the previous example. To ensure that the code does not crash in the event of a problem, place the main code in the try block and have the except block print any error messages to stdout:

import smtplib, ssl

smtp_server = "smtp.gmail.com"
port = 587  # For starttls
sender_email = "[email protected]"
password = input("Type your password and press enter: ")

# Create a secure SSL context
context = ssl.create_default_context()

# Try to log in to server and send email
try:
    server = smtplib.SMTP(smtp_server,port)
    server.ehlo() # Can be omitted
    server.starttls(context=context) # Secure the connection
    server.ehlo() # Can be omitted
    server.login(sender_email, password)
    # TODO: Send email here
except Exception as e:
    # Print any error messages to stdout
    print(e)
finally:
    server.quit() 
Copy the code

To notify the server to let it know about itself, call.helo() (SMTP) or.ehlo() (ESMTP) after creating the.smtp () object and again after.starttls(). This function is called implicitly by.starttls() and.sendmail(), so you don’t have to explicitly call.helo() or.ehlo() unless you want to check the server’s SMTP service extension.

Send your plain text email

After initiating a secure SMTP connection using either of the above methods, you can send email using.sendmail() :

server.sendmail(sender_mail, receiver_email, message)
Copy the code

I recommend defining the email address and message content at the top of the script after import so you can easily change them:

sender_email = "[email protected]"
receiver_email = "[email protected]"
message = """\ Subject: Hi there This message is sent from Python."""

# Send email here
Copy the code

The message string begins with “Subject: Hi there” followed by two newlines (\ n). This ensures that Hi there is displayed as the subject of the email and that the text following a new line is treated as the body of the message.

The following code example uses SMTP_SSL() to send plain text E-mail:

import smtplib, ssl

port = 465  # For SSL
smtp_server = "smtp.gmail.com"
sender_email = "[email protected]"  # Enter your address
receiver_email = "[email protected]"  # Enter receiver address
password = input("Type your password and press enter: ")
message = """\ Subject: Hi there This message is sent from Python."""

context = ssl.create_default_context()
with smtplib.SMTP_SSL(smtp_server, port, context=context) as server:
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, message)
Copy the code

This is a code example of sending plain text E-mail by using an encrypted SMTP connection with.starttls(). By contrast, server.ehlo() lines can be omitted because they will be called implicitly by.starttls() and.sendmail() if needed:

import smtplib, ssl

port = 587  # For starttls
smtp_server = "smtp.gmail.com"
sender_email = "[email protected]"
receiver_email = "[email protected]"
password = input("Type your password and press enter:")
message = """\ Subject: Hi there This message is sent from Python."""

context = ssl.create_default_context()
with smtplib.SMTP(smtp_server, port) as server:
    server.ehlo()  # Can be omitted
    server.starttls(context=context)
    server.ehlo()  # Can be omitted
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, message)
Copy the code

Send fancy emails

Python’s built-in email package allows you to build more fancy emails that can then be transferred using Smptlib. Below, you’ll learn how to use an email package to send an email containing HTML content and attachments.

Include HTML content

HTML is handy if you want to format text (bold, italic, etc.) in an email, or if you want to add any images, hyperlinks, or responsive content. The most common type of E-mail today is MIME (Multipurpose Internet Mail Extension) multipart E-mail, which combines HTML and plain text. MIME messages are processed by Python’s email.mime module. See the documentation for detailed instructions.

Because not all E-mail clients display HTML content by default, and some people choose to receive only plain text E-mail for security reasons, it is important to add a plain text alternative to HTML messages. Since the E-mail client will render the last part of the attachment first, be sure to add the HTML message after the plain text version.

In the following example, our MIMEText() object will contain both HTML and plain text versions of the message, and the MIMEMultipart (“alternative”) instance combines these into a message with two alternate render options:

import smtplib, ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

sender_email = "[email protected]"
receiver_email = "[email protected]"
password = input("Type your password and press enter:")

message = MIMEMultipart("alternative")
message["Subject"] = "multipart test"
message["From"] = sender_email
message["To"] = receiver_email

# Create the plain-text and HTML version of your message
text = """\
Hi,
How are you?
Real Python has many great tutorials:
www.realpython.com"""
html = """\   

Hi,

How are you?

Real Python has many great tutorials.

"""
# Turn these into plain/html MIMEText objects part1 = MIMEText(text, "plain") part2 = MIMEText(html, "html") # Add HTML/plain-text parts to MIMEMultipart message # The email client will try to render the last part first message.attach(part1) message.attach(part2) # Create secure connection with server and send email context = ssl.create_default_context() with smtplib.SMTP_SSL("smtp.gmail.com".465, context=context) as server: server.login(sender_email, password) server.sendmail( sender_email, receiver_email, message.as_string() ) Copy the code

In this example, you first define plain text and HTML messages as string literals and then store them as plain/HTML MIMEText objects. These can then be added sequentially to the MIMEMultipart (“alternative”) message and sent over a secure connection to the E-mail server. Remember to add the HTML message after the plain text instead, because the email client will try to render the last child first.

useemailAdd attachments

In order to send a binary file to an E-mail server designed to process text data, it needs to be encoded before transmission. This is usually done using Base64, which encodes binary data into printable ASCII characters.

The following code example shows how to attach a PDF file when sending an email:

import email, smtplib, ssl

from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

subject = "An email with attachment from Python"
body = "This is an email with attachment sent from Python"
sender_email = "[email protected]"
receiver_email = "[email protected]"
password = input("Type your password and press enter:")

# Create a multipart message and set headers
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject
message["Bcc"] = receiver_email  # Recommended for mass emails

# Add body to email
message.attach(MIMEText(body, "plain"))

filename = "document.pdf"  # In same directory as script

# Open PDF file in binary mode
with open(filename, "rb") as attachment:
    # Add file as application/octet-stream
    # Email client can usually download this automatically as attachment
    part = MIMEBase("application"."octet-stream")
    part.set_payload(attachment.read())

# Encode file in ASCII characters to send by email    
encoders.encode_base64(part)

# Add header as key/value pair to attachment part
part.add_header(
    "Content-Disposition".f"attachment; filename= {filename}".)# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()

# Log in to server using secure context and send email
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com".465, context=context) as server:
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, text)
Copy the code

MIMEultipart() accepts key/value pair arguments in the style of RFC5233, which are stored in the dictionary and passed to the.add_header method of the Message base class.

See the documentation for Python’s email.mime module for more information about using MIME classes.


Send multiple personalized emails

Imagine that you want to send emails to members of your organization to remind them to pay for their donations. Or, you might want to send personalized emails to students in your class with scores from recent assignments. These tasks are easy in Python.

Create CSV files using personal information

A simple way to start sending multiple personalized emails is to create CSV (comma-separated values) files containing all the required personal information. (Make sure you don’t share other people’s private information without their consent.) A CSV file can be thought of as a simple table, where the first row usually contains column headings.

Here is the contents of the contacts_file.csv file, which I saved in the same folder as the Python code. It contains the names, addresses, and grades of a set of fictional characters. I use [email protected] construct to ensure that all emails end up in my own inbox, in this case [email protected]:

name,email,grade
Ron Obvious,[email protected],B+
Killer Rabbit of Caerbannog,[email protected],A
Brian Cohen,[email protected],C
Copy the code

When creating CSV files, be sure to separate your values with commas and not include any Spaces.

Traverse a row to send multiple messages

The following code example shows how to open a CSV file and loop through its content lines (skipping the header lines). To make sure the code works before you send an email to all your contacts. I have printed Sending email to… for each contact. , we can replace them later with the ability to actually send email:

import csv

with open("contacts_file.csv") as file:
    reader = csv.reader(file)
    next(reader)  # Skip header row
    for name, email, grade in reader:
        print(f"Sending email to {name}")
        # Send email here
Copy the code

In the example above, use open(filename) as file: make sure your file is closed at the end of the code block. Csv.reader () reads the CSV file line by line and extracts its values. Next (Reader) skips the header line and the next line for name, email, grade in Reader: uses Python unpack and stores the result value in the name, email, and grade of the current contact.

If the values in the CSV file contain Spaces on either side or both sides, they can be removed using the.strip() method.

Personalized content

You can use str.format() to fill in the brace placeholders to put personalized content into the message. For example, “Hi {name}, you {result} your assignment”. Format (name=”John”, result=”passed”) You passed your assignment.

Since Python 3.6, string formatting can be done more elegantly using f-strings, but this requires defining placeholders before the F-string itself. To define E-mail messages at the beginning of the script and fill in placeholders for each contact as the CSV file is looped, use the older.format() method.

With this in mind, you can set up a common message body with custom placeholders for individuals.

Code case

The following code example allows you to send personalized emails to multiple contacts. It loops the CSV file to show each contact’s name, email, and score, as shown in the example above.

Regular messages are defined at the beginning of the script, and for each contact in the CSV file, the {name} and {grade} placeholders are filled in, and personalized emails are sent over a secure connection to the Gmail server, as you’ve seen before:

import csv, smtplib, ssl

message = """Subject: Your grade Hi {name}, your grade is {grade}"""
from_address = "[email protected]"
password = input("Type your password and press enter: ")

context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com".465, context=context) as server:
    server.login(from_address, password)
    with open("contacts_file.csv") as file:
        reader = csv.reader(file)
        next(reader)  # Skip header row
        for name, email, grade in reader:
            server.sendmail(
                from_address,
                email,
                message.format(name=name,grade=grade),
            )
Copy the code

Yagmail

There are several libraries that make it easier to send E-mail, such as Envelopes, Flanker, and Yagmail. Designed specifically for Gmail, Yagmail greatly simplifies the process of sending email with a friendly API, as shown in the following code example:

import yagmail

receiver = "[email protected]"
body = "Hello there from Yagmail"
filename = "document.pdf"

yag = yagmail.SMTP("[email protected]")
yag.send(
    to=receiver,
    subject="Yagmail test with attachment",
    contents=body, 
    attachments=filename,
)
Copy the code

This code example sends an email with a PDF attachment, much less code than our example using Email and smtplib.

When setting up Yagmail, you can add Gmail authentication to the operating system’s keyring, as described in the documentation. If you don’t, Yagmail will prompt you for your password when needed and automatically store it in the keyring.


Transactional mail services

If you plan to send a lot of email, want to view email statistics, and want to ensure reliable delivery, you may want to look at transactional email services. While all of the following services offer paid packages for bulk email, they also offer free packages so you can try them out. Some of these free plans are available indefinitely and may be sufficient for your email needs.

Below is an overview of free plans for some major transactional email services. Clicking on the provider name takes you to the pricing section of their web site.

supplier A free meal
Sendgrid 40,000 free for the first 30 days, then 100 per day
Sendinblue 200 / day
Mailgun The first 10,000 are free
Mailjet 200 / day
Amazon SES 62000 / month

You can run a Google search to see which providers best meet your needs, or just try out some free plans to see which apis you like best.


Sendgrid code example

Here is a code example of sending email using Sendgrid that shows you how to use Python’s transactional email service:

import os
import sendgrid
from sendgrid.helpers.mail import Content, Email, Mail

sg = sendgrid.SendGridAPIClient(
    apikey=os.environ.get("SENDGRID_API_KEY")
)
from_email = Email("[email protected]")
to_email = Email("[email protected]")
subject = "A test email from Sendgrid"
content = Content(
    "text/plain"."Here's a test email sent through Python"
)
mail = Mail(from_email, subject, to_email, content)
response = sg.client.mail.send.post(request_body=mail.get())

# The statements below can be included for debugging purposes
print(response.status_code)
print(response.body)
print(response.headers)
Copy the code

To run this code, you must first:

  • Sign up for a (free) Sendgrid account
  • Request an API key for user authentication
  • By typing at the command promptYour setx SENDGRID_API_KEY "YOUR_API_KEY"To add an API key, or to setSENDGRID_API_KEY YOUR_API_KEYTo store it only for the current client session

More information on how to set up Sendgrid on Mac and Windows can be found in the README repository on Github.


conclusion

You can now launch a secure SMTP connection and send multiple personalized emails to people in your contact list!

You’ve learned how to send HTML emails using plain text alternatives and attach files to your emails. The Yagmail package simplifies all of these tasks when you use a Gmail account. If you plan to send a lot of email, it’s worth investigating transactional email services.

Enjoy sending email in Python, but remember: please don’t spam!

Pay close attention to the public number < code and art >, learn more foreign high-quality technical articles.