How to send beautiful emails with attachments (yes, cat pics too) using only Python
David MM👨🏻💻
Posted on September 25, 2019
Original post: https://letslearnabout.net/tutorial/how-to-send-beautiful-emails-with-attachments-using-only-python/
How to send beautiful emails with attachments (yes, cat pics too) using only Python
Did you know you can send emails using Python? Not only that, you can send files attached too!
And instead of using the black-text-white-background plain text most people use, you can send your emails as HTML files, to add some flavour to your emails, like this one:
In just a few minutes, you will learn how to create an SMTP server to send emails using Python to a list of clients, students, friends…
In this video you'll learn how to:
- Send one email to one person
- Add information such as your name, the receiver name or a subject to make it more human-like
- Send one email to multiple people
- Make a beautiful email using HTML
- Attach files to the emails we sent
Setting up everything
We are going to send emails using a Gmail account. Before anything, create a gmail account (or use your own) and enable the "Less secure app access" in your account. You can do it here: https://myaccount.google.com/u/0/security?hl=en&pli=1
After that, you are set! Create an environment with Python (I use pipenv, so _pipenv shell) _and you are ready to go!
Simple, one to one email sender
Let's start by creating a simple script that sends one text email to other people from your account (I use pipenv, so I create one with pipenv_ shell_):
Create a python file, for example email_server.py, and write:
import smtplib, ssl
# User configuration
sender_email = YOUR_EMAIL
receiver_email = RECEIVER_EMAIL
password = input('Please, type your password:n')
# Email text
email_body = '''
This is a test email sent by Python. Isn't that cool?
'''
smtp is a Python module we will use to create an SMTP (Simple Mail Transfer Protocol) session object that will send any email we want. ssl module provides access to TLS (Transport Layer Security) to help us making it more secure.
Then, we'll add some variables such as the sender and receiver email, a prompt to the user to enter the email password and a body to the email. Replace the values in uppercase with yours. For testing purposes, I use the same email.
After having our configuration, let's write the function that will send the emails:
print("Sending the email...")
try:
# Creating a SMTP session | use 587 with TLS, 465 SSL and 25
server = smtplib.SMTP('smtp.gmail.com', 587)
# Encrypts the email
context = ssl.create_default_context()
server.starttls(context=context)
# We log in into our Google account
server.login(sender_email, password)
# Sending email from sender, to receiver with the email body
server.sendmail(sender_email, receiver_email, email_body)
print('Email sent!')
except Exception as e:
print(f'Oh no! Something bad happened!n {e}')
finally:
print('Closing the server...')
server.quit()
This function will:
- Create a smtplib.SMTP instance. We use gmail so we send it using the host smtp.gmail.com and the port 587. You can use other providers (such as hotmail) by using their host and port.
- Encrypt the email using the _starttl_s method.
- Log into our Google email account
- Send the email using the sender email, to the receiver email, and the message is just a plain text.
- Use a try, except to catch all the errors and _finallyi_close the SMTP instance.
Run the code now and you'll receive your email!
But we are not here to send just an email. We can do that ourselves!
Let's send now multiple emails to a list.
Sending emails to a list of people
To send multiple emails, we only need to perform a few tweaks here and there. Let's also make our emails more personal adding a subject to the email and information about the sender:
First, let's write more human-like emails:
import smtplib, ssl
from email.mime.text import MIMEText # New line
from email.utils import formataddr # New line
# User configuration
sender_email = YOUR_EMAIL
sender_name = YOUR NAME # New line
password = input('Please, type your password:n')
receiver_email = RECEIVER_EMAIL
receiver_name = RECEIVER_NAME # New line
Add the lines with the '# New line' comment. We are importing the MIMEText( Multi-Purpose Internet Mail Extensions ) to configure our emails and a _formataddr_helper method.
After the first print but before the try, add this configuration:
# Configurating user's info
msg = MIMEText(email_body, 'plain')
msg['To'] = formataddr((receiver_name, receiver_email))
msg['From'] = formataddr((sender_name, sender_email))
msg['Subject'] = 'Hello, my friend ' + receiver_name
Now, we create a MIMEText instance with email_body as plain text, we set our 'To' text to the receiver name and email, the 'From' text to our name and email and the subject to 'Hello, my friend NAME'.
Finally, modify the sender.sendmail method to:
server.sendmail(sender_email, receiver_email, msg.as_string())
Send the email again:
Nice, now has less spammy vibes. Let's send that 100% normal, not-fake, email to a list of people:
# receiver_email = RECEIVER_EMAIL
# receiver_name = RECEIVER_NAME
receiver_emails = [RECEIVER_EMAIL_1, RECEIVER_EMAIL_2, RECEIVER_EMAIL_3]
receiver_names = [RECEIVER_NAME_1, RECEIVER_NAME_2, RECEIVER_NAME_3]
As we are going to iterate over the receiver emails and names, precede the code bellow the _receiver_names _variable with this for-loop and tab the code so it belongs to the loop:
for receiver_email, receiver_name in zip(receiver_emails, receiver_names):
print("Sending the email...")
# Configurating user's info
msg = MIMEText(email_body, 'plain')
...
This should be your code right now:
import smtplib
import ssl
from email.mime.text import MIMEText # New line
from email.utils import formataddr # New line
# User configuration
sender_email = YOUR_EMAIL
sender_name = YOUR NAME
password = input('Please, type your password:n')
receiver_emails = [RECEIVER_EMAIL_1, RECEIVER_EMAIL_2, RECEIVER_EMAIL_3]
receiver_names = [RECEIVER_NAME_1, RECEIVER_NAME_2, RECEIVER_NAME_3]
# Email text
email_body = '''
This is a test email sent by Python. Isn't that cool?
'''
for receiver_email, receiver_name in zip(receiver_emails, receiver_names):
print("Sending the email...")
# Configurating user's info
msg = MIMEText(email_body, 'plain')
msg['To'] = formataddr((receiver_name, receiver_email))
msg['From'] = formataddr((sender_name, sender_email))
msg['Subject'] = 'Hello, my friend ' + receiver_name
try:
# Creating a SMTP session | use 587 with TLS, 465 SSL and 25
server = smtplib.SMTP('smtp.gmail.com', 587)
# Encrypts the email
context = ssl.create_default_context()
server.starttls(context=context)
# We log in into our Google account
server.login(sender_email, password)
# Sending email from sender, to receiver with the email body
server.sendmail(sender_email, receiver_email, msg.as_string())
print('Email sent!')
except Exception as e:
print(f'Oh no! Something bad happened!n {e}')
finally:
print('Closing the server...')
server.quit()
Run the code and your email will be send to 3 different email address!
Here we are sending an email to 3 different people, but you can send it to hundreds of your students, followers, blog readers, etc! And…
…they will get an ugly email.
I promised you sending beautiful emails with Python that can draw people to whatever you want. So let's go!
Sending beautiful emails with HTML
It's pretty simple. Now we are sending a string as a _email_body, _right?
Instead of that, we are going to send a string that contains HTML code.
First, let's create our HTML file. Use a code editor such as VS Code or Atom, or create it with an online editor such as .
Save it into an HTML file (mine is email.html).
Now, place email_body with this:
# Email body
email_html = open('email.html')
email_body = email_html.read()
Replace the old line with the new one:
# msg = MIMEText(email_body, 'plain') # Old line
msg = MIMEText(email_body, 'html') # New line
Run the code and this is the result:
Pretty cool, right?
But what if we want to send also invoices, pdfs, excel files, videos, or some other file?
Add files to your emails – The mandatory cat.gif example
To add files, instead of sending one object, the HTML, we are going to send multiple parts. One being the HTML, other the gif. To do so, add this:
import smtplib, ssl
from email.mime.text import MIMEText
from email.utils import formataddr
from email.mime.multipart import MIMEMultipart # New line
from email.mime.base import MIMEBase # New line
from email import encoders # New line
....
# Email body
email_html = open('email.html')
email_body = email_html.read()
filename = 'cat.gif' # New line
Remember to add a file called 'cat.gif' to the folder or replace it with one of your own.
Replace the _msg = MIMEText(email_body, 'html') _creation with this:
msg = MIMEMultipart()
msg['To'] = formataddr((receiver_name, receiver_email))
msg['From'] = formataddr((sender_name, sender_email))
msg['Subject'] = 'Hello, my friend ' + receiver_name
msg.attach(MIMEText(email_body, 'html'))
This will create the first part of the body, the one with the HTML, now let's add the attachment:
try:
# Open PDF file in binary mode
with open(filename, "rb") 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}",
)
msg.attach(part)
except Exception as e:
print(f'Oh no! We didn't found the attachment!n{e}')
This will:
- Load the file attached
- Encode it
- Add it to the header as attachment
- Add the part to the MIMEMultipart
- Throw and error if the file isn't found.
Check your code with mine:
import smtplib
import ssl
from email.mime.text import MIMEText
from email.utils import formataddr
from email.mime.multipart import MIMEMultipart # New line
from email.mime.base import MIMEBase # New line
from email import encoders # New line
# User configuration
sender_email = YOUR_EMAIL
sender_name = YOUR_NAME
password = input('Please, type your password:n')
receiver_emails = [RECEIVER_EMAIL_1, RECEIVER_EMAIL_2, RECEIVER_EMAIL_3]
receiver_names = [RECEIVER_NAME_1, RECEIVER_NAME_2, RECEIVER_NAME_3]
# Email body
email_html = open('email.html')
email_body = email_html.read()
filename = 'cat.gif'
for receiver_email, receiver_name in zip(receiver_emails, receiver_names):
print("Sending the email...")
# Configurating user's info
msg = MIMEMultipart()
msg['To'] = formataddr((receiver_name, receiver_email))
msg['From'] = formataddr((sender_name, sender_email))
msg['Subject'] = 'Hello, my friend ' + receiver_name
msg.attach(MIMEText(email_body, 'html'))
try:
# Open PDF file in binary mode
with open(filename, "rb") 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}",
)
msg.attach(part)
except Exception as e:
print(f'Oh no! We didn't found the attachment!n{e}')
break
try:
# Creating a SMTP session | use 587 with TLS, 465 SSL and 25
server = smtplib.SMTP('smtp.gmail.com', 587)
# Encrypts the email
context = ssl.create_default_context()
server.starttls(context=context)
# We log in into our Google account
server.login(sender_email, password)
# Sending email from sender, to receiver with the email body
server.sendmail(sender_email, receiver_email, msg.as_string())
print('Email sent!')
except Exception as e:
print(f'Oh no! Something bad happened!n{e}')
break
finally:
print('Closing the server...')
server.quit()
Run the code… and now the attachment is in the email!
Conclusion
Writing code that lets you send an email to your email list, with attachment files and way more attractive than the boring plain black-text-white-background formula, is easy.
While this is a one-file script, you can easily incorporate it to an actual Python framework (Flask, Django….) web app, another program you are actually using or improve the current one to perform more functions.
Creating and sending emails never has been so easy with Python!
Posted on September 25, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.