Sending Emails With Python
Sending a Plain-Text Email
Before we dive into sending emails with HTML content and attachments, you’ll learn to send plain-text emails using Python. These are emails that you could write up in a simple text editor. There’s no fancy stuff like text formatting or hyperlinks. You’ll learn that a bit later.
Starting a Secure SMTP Connection
When you send emails through Python, you should make sure that your SMTP connection is encrypted, so that your message and login credentials are not easily accessed by others. SSL (Secure Sockets Layer) and TLS (Transport Layer Security) are two protocols that can be used to encrypt an SMTP connection. It’s not necessary to use either of these when using a local debugging server.
There are two ways to start a secure connection with your email server:
- Start an SMTP connection that is secured from the beginning using
SMTP_SSL()
. - Start an unsecured SMTP connection that can then be encrypted using
.starttls()
.
In both instances, Gmail will encrypt emails using TLS, as this is the more secure successor of SSL. As per Python’s Security considerations, it is highly recommended that you use create_default_context()
from the ssl
module. This will load the system’s trusted CA certificates, enable host name checking and certificate validation, and try to choose reasonably secure protocol and cipher settings.
If you want to check the encryption for an email in your Gmail inbox, go to More → Show original to see the encryption type listed under the Received header.
smtplib
is Python’s built-in module for sending emails to any Internet machine with an SMTP or ESMTP listener daemon.
I’ll show you how to use SMTP_SSL()
first, as it instantiates a connection that is secure from the outset and is slightly more concise than the .starttls()
alternative. Keep in mind that Gmail requires that you connect to port 465 if using SMTP_SSL()
, and to port 587 when using .starttls()
.
Option 1: Using SMTP_SSL()
The code example below creates a secure connection with Gmail’s SMTP server, using the SMTP_SSL()
of smtplib
to initiate a TLS-encrypted connection. The default context of ssl
validates the host name and its certificates and optimizes the security of the connection. Make sure to fill in your own email address instead of my@gmail.com
:
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("my@gmail.com", password)
# TODO: Send email here
Using with smtplib.SMTP_SSL() as server:
makes sure that the connection is automatically closed at the end of the indented code block. If port
is zero, or not specified, .SMTP_SSL()
will use the standard port for SMTP over SSL (port 465).
It’s not safe practice to store your email password in your code, especially if you intend to share it with others. Instead, use input()
to let the user type in their password when running the script, as in the example above. If you don’t want your password to show on your screen when you type it, you can import the getpass
module and use .getpass()
instead for blind input of your password.
Option 2: Using .starttls()
Instead of using .SMTP_SSL()
to create a connection that is secure from the outset, we can create an unsecured SMTP connection and encrypt it using .starttls()
.
To do this, create an instance of smtplib.SMTP
, which encapsulates an SMTP connection and allows you access to its methods. I recommend defining your SMTP server and port at the beginning of your script to configure them easily.
The code snippet below uses the construction server = SMTP()
, rather than the format with SMTP() as server:
which we used in the previous example. To make sure that your code doesn’t crash when something goes wrong, put your main code in a try
block, and let an except
block print any error messages to stdout
:
import smtplib, ssl
smtp_server = "smtp.gmail.com"
port = 587 # For starttls
sender_email = "my@gmail.com"
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()
To identify yourself to the server, .helo()
(SMTP) or .ehlo()
(ESMTP) should be called after creating an .SMTP()
object, and again after .starttls()
. This function is implicitly called by .starttls()
and .sendmail()
if needed, so unless you want to check the SMTP service extensions of the server, it is not necessary to use .helo()
or .ehlo()
explicitly.
Sending Your Plain-text Email
After you initiated a secure SMTP connection using either of the above methods, you can send your email using .sendmail()
, which pretty much does what it says on the tin:
server.sendmail(sender_email, receiver_email, message)
I recommend defining the email addresses and message content at the top of your script, after the imports, so you can change them easily:
sender_email = "my@gmail.com"
receiver_email = "your@gmail.com"
message = """\
Subject: Hi there
This message is sent from Python."""
# Send email here
The message
string starts with "Subject: Hi there"
followed by two newlines (\n
). This ensures Hi there
shows up as the subject of the email, and the text following the newlines will be treated as the message body.
The code example below sends a plain-text email using SMTP_SSL()
:
import smtplib, ssl
port = 465 # For SSL
smtp_server = "smtp.gmail.com"
sender_email = "my@gmail.com" # Enter your address
receiver_email = "your@gmail.com" # 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)
For comparison, here is a code example that sends a plain-text email over an SMTP connection secured with .starttls()
. The server.ehlo()
lines may be omitted, as they are called implicitly by .starttls()
and .sendmail()
, if required:
import smtplib, ssl
port = 587 # For starttls
smtp_server = "smtp.gmail.com"
sender_email = "my@gmail.com"
receiver_email = "your@gmail.com"
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)