Sending Email in Python: A Guide

  • Table of contents

Send emails in Python using SMTP mailer with this tutorial. Integrate email functionality into web apps and experiment on a test server.

Introduction

To extend the functionality of any web application, it's essential to integrate the sending of notifications and emails. For such cases, Python has a built-in smtplib module for sending emails using Simple Mail Transfer Protocol (SMTP). In this tutorial, we'll discuss all the necessary steps to send emails through an SMTP server using Python programming language. We will also learn about the different kinds of sending emails, from emails with images and attachments to multiple emails at once.

How to send an email using SMTP?

The standard Python library includes an SMTP module for sending emails. There is no need for additional settings or procedures. The module can be imported using the following statement:

import smtplib

Type the following into an interactive Python process to make sure that the module has been successfully imported and to get a complete description of its classes and arguments:

help(smtplib)

Python SMTP server for email testing

To avoid spamming your mail and customers, it is important to experiment on a test server when creating a new application or adding features for the first time. That way, your domain will not be on the block due to spam.

Local SMTP server

You may test email functionality using Python's pre-installed smtpd module. It discards emails and prints their content to the console instead of sending them. Running a local debugging server eliminates the need to encrypt messages or log into an email server.

In Command Prompt, type:

python -m smtpd -n -c DebuggingServer localhost:1025

To run the SMTP server on port 25, you need access to root on Linux:

sudo python -m smtpd -n -c DebuggingServer localhost:25

It helps you test your code and identify any flaws. However, you won't be able to check your HTML email template.

How to send HTML email?

In most cases, you need to add some formatting, links, or images to your email notifications. We can simply put all of these with the HTML content. For this purpose, Python has an email package.

We will deal with the MIME message type, which is able to combine HTML and plain text. In Python, it is handled by email.mime module.

It is better to write text and HTML versions separately and then merge them with the MIMEMultipart("alternative") instance. It means that such a message has two rendering options accordingly. In case an HTML isn't rendered successfully for some reason, a text version will still be available.

# import ing the modules
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

port = 2525
smtp_server = "smtp.example.com" # write your server's name
login = "1a2b3c4d5e6f7g" # paste your login
password = "1a2b3c4d5e6f7g" # paste your password
sender_email = "me@example.com"
receiver_email = "you@example.com"
message = MIMEMultipart("alternative")
message["Subject"] = "multipart test"
message["From"] = sender_email
message["To"] = receiver_email
# write the plain text part
text = """\
Hi,
Check out the new post about using Python for sending emails:
SMTP Server for Testing: Cloud-based or Local?
/blog/2018/09/27/cloud-or-local-smtp-server/
Feel free to let us know what content would be useful for you!"""
# write the HTML part
html = """\
<html>
  <body>
    <p>Hi,<br>
       Check out the new post about using Python for sending emails:</p>
    <p><a href="/blog/2018/09/27/cloud-or-local-smtp-server">SMTP Server for Testing: Cloud-based or Local?</a></p>
    <p> Feel free to <strong>let us</strong> know what content would be useful for you!</p>
  </body>
</html>
"""
# convert both parts to MIMEText objects and add them to the MIMEMultipart message
part1 = MIMEText(text, "plain")
part2 = MIMEText(html, "html")
message.attach(part1)
message.attach(part2)
# send your email
with smtplib.SMTP("smtp.example.com", 2525) as server:
    server.login(login, password)
    server.sendmail(
        sender_email, receiver_email, message.as_string()
    )
print('Sent')

Sending emails with attachments using Python

Let's learn how to attach various files to emails using Python. Python allows us to attach text, images, audio files, and applications. Attachments are MIME objects but must be base64 encoded. Use email.mime.audio.MIMEAudio or email.mime.image.MIMEImage to attach a file. Remember that you should not send files larger than 20 MB, although it is possible.

Receipts, tickets, boarding passes, order confirmations, etc., are usually sent in PDF format.

Let's see how to send boarding passes in PDF format:

import smtplib

# import the needed modules
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

port = 2525
smtp_server = "smtp.example.com" # put your server's name
login = "1a2b3c4d5e6f7g" # paste your login
password = "1a2b3c4d5e6f7g" # paste your password

subject = "An example of boarding pass"
sender_email = "me@example.com"
receiver_email = "you@example.com"

message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject

# Add body to email
body = "This is an example of how we can send a boarding pass in attachment using Python"
message.attach(MIMEText(body, "plain"))

filename = "yourBP.pdf"
# Open PDF file in binary mode

# We assume that the file is in the directory where you run your Python script from
with open(filename, "rb") as attachment:
    # The content type "application/octet-stream" means that a MIME attachment is a binary file
    part = MIMEBase("application", "octet-stream")
    part.set_payload(attachment.read())

# Encode to base64
encoders.encode_base64(part)

# Add header
part.add_header(
    "Content-Disposition",
    f"attachment; filename= {filename}",
)

# Add attachment to your message and convert it to string
message.attach(part)
text = message.as_string()

# send your email
with smtplib.SMTP("smtp.example.com", 2525) as server:
    server.login(login, password)
    server.sendmail(
        sender_email, receiver_email, text
    )
print('Sent') 

To attach multiple files, you can use the message.attach() method several times.

Emails with images

Images are attachments, even if they are included in the message body. They come in three different varieties: linked images, base64 images, and CID attachments, which are all embedded as MIME objects. In this section, we'" cover their quirks, advantages and disadvantages, and compatibility with most email clients.

To add a CID attachment, we will create a multi-part MIME message with the MIMEImage component:

# import all necessary components
import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

port = 2525
smtp_server = "smtp.example.com" # type your server's name
login = "1a2b3c4d5e6f7g" # paste your login
password = "1a2b3c4d5e6f7g" # paste your password

sender_email = "me@example.com"
receiver_email = "you@example.com"
message = MIMEMultipart("alternative")
message["Subject"] = "CID image test"
message["From"] = sender_email
message["To"] = receiver_email

# write the HTML part
html = """\
<html>
 <body>
   <img src="cid:Mailtrapimage">
 </body>
</html>
"""

part = MIMEText(html, "html")
message.attach(part)

# We assume that the image file is in the same directory that you run your Python script from
fp = open('mailtrap.jpg', 'rb')
image = MIMEImage(fp.read())
fp.close()

# Specify the  ID according to the img src in the HTML part
image.add_header('Content-ID', '<Mailtrapimage>')
message.attach(image)

# send your email
with smtplib.SMTP("smtp.example.com", 2525) as server:
   server.login(login, password)
   server.sendmail(
       sender_email, receiver_email, message.as_string()
   )
print('Sent')

The CID picture is shown as an attachment and a component of an HTML message. However, many email applications, most notably Gmail, don't often display CID pictures since messages containing this image format are frequently seen as spam.

So let's look at how to attach a base64-encoded image.

# import the necessary components
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import base64

port = 2525
smtp_server = "smtp.example.com" # paste your server's name
login = "1a2b3c4d5e6f7g" # paste your login
password = "1a2b3c4d5e6f7g" # paste your password

sender_email = "me@example.com"
receiver_email = "you@example.com"
message = MIMEMultipart("alternative")
message["Subject"] = "inline embedding"
message["From"] = sender_email
message["To"] = receiver_email

# We assume that the image file is in the same directory that you run your Python script from
encoded = base64.b64encode(open("mailtrap.jpg", "rb").read()).decode()

html = f"""\
<html>
 <body>
   <img src="data:image/jpg;base64,{encoded}">
 </body>
</html>
"""

part = MIMEText(html, "html")
message.attach(part)

# send your email
with smtplib.SMTP("smtp.example.com", 2525) as server:
   server.login(login, password)
   server.sendmail(
       sender_email, receiver_email, message.as_string()
   )
print('Sent')

The image is now embedded in the HTML message and is not available as an attachment and will not show up as spam.

Sending multiple emails using Python

The advantage of Python email is that you can send multiple emails to different recipients and personalize each one. Just insert the addresses of additional recipients, separating them with commas, and add CCs and BCCs. But Python loops will be handy if you're sending out bulk mail. One method is to create a database in.csv format and save it in the same directory as your Python script.

Let's create a simplest table with only two columns—name and email address—from the list:

#name,email
John Johnson,john@johnson.com
Peter Peterson,peter@peterson.com

The code below will open the file, loop through its rows line by line, and replace the value of the "name" column with the "name" placeholder.

import csv, smtplib

port = 2525
smtp_server = "smtp.example.com"
login = "1a2b3c4d5e6f7g" # paste your login
password = "1a2b3c4d5e6f7g" # paste your password

message = """Subject: Order confirmation
To: {recipient}
From: {sender}

Hi {name}, thanks for your order! We are processing it now and will contact you soon"""
sender = "you@example.com"

with smtplib.SMTP("smtp.example.com", 2525) as server:
    server.login(login, password)
    with open("contacts.csv") as file:
        reader = csv.reader(file)
        next(reader)  # it skips the header row
        for name, email in reader:
            server.sendmail(
               sender,
                email,
                message.format(name=name, recipient=email, sender=sender)
            )
            print(f'Sent to {name}')

We receive the following outcome after executing the script:

Sent to John Johnson
Sent to Peter Peterson

Sending emails via Gmail using Python

Set up your business server when you're ready to send emails to actual recipients. Your localhost or any external SMTP relies on your demands, objectives, and tastes. Let's look deeper at Gmail, one of the most well-liked choices. To use the Gmail server, you'll need to know:

server name = smtp.gmail.com
port = 465 for SSL/TLS connection (preferred)
Or port = 587 for STARTTLS connection.
username = your Gmail email address
password = your password.

You can use Yagmail, a specialized Gmail/SMTP, if you want something simple. Just compare the above examples with these few lines of code:

import smtplib, ssl

port = 465
password = input("your password")
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", port, context=context) as server:
    server.login("my@gmail.com", password)

Or using Yagmail:

import yagmail
yag = yagmail.SMTP()
contents = [
    "This is the body, and here is just text http://somedomain/image.png",
    "You can find an audio file attached.", '/local/path/to/song.mp3'
]
yag.send('to@someone.com', 'subject', contents)

Conclusion

So in this tutorial, we have demonstrated the essential alternatives for sending emails using Python. Studying Python documentation and just playing with your code are two things we advise you to do to get great results.