Sending Multiple Emails From a CSV File
You may come across the situation, where you want to send a certain message to different contacts. Maybe you also need to slightly change the message for each receiver. In this video, you’ll learn how to use CSV files to do exactly that! In this case you are a teacher, who wants to inform his students about their grades.
00:00 So now you can format your emails and add attachments, but if you want to send these to multiple people, you currently have to change the code each time, which is not ideal.
00:09
In this video, you’ll learn to use a CSV file as an input so that you can fire off a bunch of emails with one script. Over here, I’ve got a contacts.csv
file that has the name, email, and grade for a couple of fake people.
00:23
Note the email addresses where I’ve added a plus (+
), and then the person’s name to my test email. When Gmail’s routing the email, it’s going to ignore anything after the +
sign, so all of these will be sent to my test email but kept separate.
00:38 This is very useful when you’re practicing sending multiple emails at once. So, let’s head back to the editor, and you’re going to start with a blank script this time.
00:47
Grab your imports. You’re going to need the csv
library, smtplib
, and ssl
. Go ahead and make your message template.
00:58
Call that message
, do a multiline string, and you’ll say """From: {sender}"""
, """To: {receiver}"""
. """Subject: Your Grades"""
.
01:21
"""Hi {name}, your grade is {grade}."""
. Our sender
will be our email address, but then the receiver
, name
, and grade
will be pulled from that CSV file.
01:36
So, let’s go ahead, make a sender
, which in my case is 'catinthehacks@gmail.com'
—and actually, that’s 'thecatinthehacks'
.
01:50
And password
. Like before, use an input()
.
02:00
Okay. Much of this will be the same. You’ll create a context
with ssl
, create a default context. Let me bring this down. Okay. With smtplib.SMTP_SSL()
—and because we’re not really messing around with these anymore, I’m just going to type them in as strings instead of setting variables for them.
02:27
Set your server, your port, make context=context
, call this your server
,
02:40
then log into that server
.
02:48
All right. So up until now, just about all of this should be a review, except now you’re going to use that CSV file. So now open('contacts.csv')
.
03:03
And just call this file
, and then make a reader
which is going to be a csv.reader()
. Pass in file
. You’re going to skip the first row because that’s just header information that we don’t need.
03:18
And then unpack that as for name, email, grade in reader:
you’re going to .sendmail()
. You need to pass in your sender
, the email
that it’s going to, and message
, which is from the message template above.
03:43
And just open that. sender
is going to be sender
, email
is going to be email
, name
is name
, and grade
is grade
.
04:00
And actually, before I send this, I think—yeah. This 'To: {receiver}'
should actually just be email
here to keep all the naming the same.
04:11 Okay! So if this works the way it should, it’ll log into the server like we’ve done before, and then it’s going to go through your CSV file and then for each row in there, it’s going to send an email based on the information that’s in that CSV.
04:23 So, save this and try it out!
04:30 Okay. Passing in the password.
04:36 We don’t see any errors, so let’s hop over. And what do we have here? Okay. So Your Grades, it looks like here’s one for Ron, here’s one for Rabbit, and you can see that the template has been changed for each of them. So this was sent to Ron, and Rabbit, and I think I got one more here. Yeah, there’s the third one there. All right!
04:59 So now you can see that with one script and a CSV file, you can send out multiple emails that are personalized. That’s pretty cool. You can use this kind of system if you need to verify somebody’s email for a website or if you’re running an email list with Python.
05:15 You do need to be careful, though, because it’s very easy to send out way too many spam emails, so this is definitely something to use responsibly. All right!
05:24 In the next video, you’re going to learn how to use Yagmail, which is a library to make working with Gmail even easier. Thanks for watching.
Joe Tatusko RP Team on July 8, 2019
You’re welcome! How do you have your code structured?
Let me know what type of error you’re seeing as well!
RetiringInComo on July 9, 2019
import email, smtplib, ssl, csv
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 = "generic@blah.com"
#receiver_email = ""
# 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 = "test.xlsx" # 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
#with smtplib.SMTP("mailhost:25") as server:
#server.sendmail(sender_email, receiver_email, text)
with smtplib.SMTP("mailhost:25") as server:
with open("contacts_file.csv") as file:
reader = csv.reader(file)
next(reader) # Skip header row
for name, email, grade in reader:
server.sendmail(
sender_email,
email,text
#message.format(name=name,grade=grade),
)
RetiringInComo on July 9, 2019
This works but sends the same message and attachment. I commented out the message.format last line since it is not supported with Mime.Base. Ultimately what I am trying to do would be to have a csv file that has name, message, attachment as the headers and use that file to send multiple emails to different people and include different attachments.
So to use your example what if you also wanted to send those three students a copy of their report (lets say the reports were excel files). Then Ron Obvious would get his email stating he got a B+ and it would also include an excel attachment, Killer Rabbit would get his grade and his own attachment etc…
Joe Tatusko RP Team on July 11, 2019
Gotcha! So what you would want to do is save the attachment filepath in your csv.
Try to structure your code something like the following:
open the csv file -> loop through the rows -> create your email object -> open attachment and attach to email -> send email
It looks to me like you’re creating the email object, attaching your file, and then looping through the contacts csv so the same email object and attachment get sent over and over. Let me know if that makes sense!
Abby Jones on July 15, 2019
I cannot proceed any further, I am getting this:
Traceback (most recent call last):
File "/home/abbyrjones72/Documents/remote_repos/emailer/email.py", line 1, in <module>
import smtplib, ssl
File "/home/abbyrjones72/anaconda3/lib/python3.7/smtplib.py", line 47, in <module>
import email.utils
File "/home/abbyrjones72/Documents/remote_repos/emailer/email.py", line 17, in <module>
with smtplib.SMTP_SSL("stmp.gmail.com", 465, context=context) as server:
AttributeError: module 'smtplib' has no attribute 'SMTP_SSL'
Here is my code:
import smtplib, ssl
import csv
message = """\
From: {sender}
To: {email}
Subject: Your Grades
Hi {name}, your grade is {grade}.
"""
sender = "abbyrjones72dev@gmail.com"
password = input("Please enter a password: ")
context = ssl.create_default_context()
with smtplib.SMTP_SSL("stmp.gmail.com", 465, context=context) as server:
server.login(sender, password)
with open("contacts.csv") as file:
reader = csv.reader(file)
# skip first header row
next(reader)
for name, email, grade in reader:
server.sendmail(
sender,
email,
message.format(sender=sender, email=email, name=name, grade=grade),
)
Dan Bader RP Team on July 15, 2019
@Abby: The problem is that your script is called email.py
which shadows (hides) Python’s built-in email
module. So instead of importing the email
module from the standard library Python is trying to import your local email.py
script—which doesn’t have an smtplib
class, hence the AttributeError
.
The easiest way to solve this is to rename your local email.py
script file to something else :) More info here in this StackOverflow thread.
RetiringInComo on July 15, 2019
Thanks for the clue, got this to work fabiously!!
import email, smtplib, ssl, csv
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from_address = ""
with smtplib.SMTP("mailhost:25") as server:
with open("contacts_file.csv") as file:
reader = csv.reader(file)
next(reader) # Skip header row
for name, subject_line, body, email, grade, attach in reader:
# Create a multipart message and set headers
message = MIMEMultipart()
message["From"] = from_address
message["To"] = email
message["Subject"] = subject_line
filename = str(attach)
# Add body to email
message.attach(MIMEText(body, "plain"))
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()
# Use server to send email
server.sendmail(from_address, email, text)
Abby Jones on July 16, 2019
No, this was only after my original file main.py did the same thing.
Joe Tatusko RP Team on July 16, 2019
That’s great! Glad you were able to work it out :D
Abby Jones on July 16, 2019
Nothing worked out lol, it doesn’t work.
Abby Jones on July 16, 2019
I’m sorry, I will figure it out.
Abby Jones on July 16, 2019
It is now working. I made some transcription errors in a few places. Frustration will do that. Thank you Dan and Joe!
Joe Tatusko RP Team on July 16, 2019
Sorry, was replying to RetiringInComo haha
Awesome that you got it too! Glad we (well, mainly Dan :P) could help out.
Abby Jones on July 16, 2019
Yes, I am going to give that link full attention after this fiasco. This really is yet another wonderful tutorial.
beauenslow on Aug. 9, 2019
Is there a way to send multiple emails from a csv file and have the message sent as html. I have been having trouble making this work. Can you help?
Joe Tatusko RP Team on Aug. 13, 2019
Sure thing! This would be similar to RetiringInComo’s question above. You need to structure your code in a way that you’re making an HTML email for each row of the CSV:
open the csv file -> loop through the rows -> create your HTML email object -> send email
Where this gets tricky is what you put in the ‘body’ column of the CSV. I’d start with adding the HTML strings directly into the CSV, then start working at how you could keep the critical information in the CSV and use formatted strings to make an HTML template that is kept outside of the CSV
Kalesis on Sept. 7, 2019
Hello, excellent course.
If with gmail to test we add “my +”, which is the simile for Office365 / Outlook, 7 Yahoo, etc.
Thank you
Become a Member to join the conversation.
RetiringInComo on July 5, 2019
Thanks Joe!!
Learned a ton , also got it to work at work on Outlook. Have one question though can’t seem to combine the Multiple Emails with Multiple Attachments? Any advice?