Sending E-Mail to various recipients with Python - python

I want to send an email to various recipients (like 10 people) with the given table in the code, but the mail only reaches the first mail address. Is there a way to code it the way, that I can send an email to various recipients?
df = pd.DataFrame(Table)
filename = str(date.today()) + ".png"
#dir = pathlib.Path(__file__).parent.absolute()
folder = r"/results/"
#path_plot = str(dir) + folder + filename
from_mail = "abcdef#gmail.com"
to_mail = 'example1#hotmail.com,example2#live.com, example2#yahoo.com, example2#yahoo.de'
smtp_server = "smtp.gmail.com"
smtp_port = 465
def send_email( smtp_server, smtp_port, from_mail, from_password, to_mail):
'''
Send results
'''
msg = MIMEMultipart()
msg['Subject'] = 'Results'
msg['From'] = from_mail
COMMASPACE = ', '
msg['To'] = COMMASPACE.join([from_mail, to_mail])
msg.preamble = 'Something special'
html = """\
<html>
<head></head>
<body>
{0}
</body>
</html>
""".format(df.to_html())
part1 = MIMEText(html, 'html')
msg.attach(part1)

The to_addr argument should be a list of strings if you want to send to multiple recipients.
The immediate problem in your code is that you are joining the from_addr with the (string or list) to_addr where you should be creating a single list to put in the recipients field.
As an aside, your code seems to be written for Python 3.5 or earlier. The email library was overhauled in 3.6 and is now quite a bit more versatile and logical. Probably throw away what you have and start over with the examples from the email documentation.
Here is a basic refactoring (just quick and dirty; the structure is rather weird - why are you passing some strings as parameters whereas others are globals outside the function?). It removes some of the problems in your code simply because the modern API removes a lot of the boilerplate which was necessary with the older one.
import pandas as pd # I'm guessing ...?
from email.message import EmailMessage
...
df = pd.DataFrame(Table)
# Commenting out unused variables
# filename = str(date.today()) + ".png"
# folder = "/results/" # no need for an r string, no backslashes here
from_mail = "abcdef#gmail.com"
from_password = cred.passwort
to_mail = ['example1#hotmail.com', 'example2#live.com', 'example2#yahoo.com', 'example2#yahoo.de']
smtp_server = "smtp.gmail.com"
smtp_port = 465
def send_email(smtp_server, smtp_port, from_mail, from_password, to_mail):
'''
Send results via mail
'''
msg = EmailMessage()
msg['Subject'] = 'Results'
msg['From'] = from_mail
msg['To'] = ', '.join(to_mail + [from_mail])
# Don't muck with the preamble, especially if you don't understand what it is
html = """\
<html>
<head></head>
<body>
{0}
</body>
</html>
""".format(df.to_html())
msg.set_content(html, 'html')
with smtplib.SMTP_SSL(smtp_server, smtp_port) as server:
server.ehlo()
server.login(from_mail, from_password)
# Some servers require a second ehlo() here
# Use send_message instead of legacy sendmail method
server.send_message(msg)
server.quit()
send_email(smtp_server, smtp_port, from_mail, from_password, to_mail)
The generated HTML is still hideous but let's just hope nobody looks at the message source.

Related

Send HTML file contents via python email script

Looking for a way to send the contents of a HTML file that is generated once a day using this script below. Running into road blocks getting it to work. I can sent HTML and see it, just not sure how to print out the contents of the file and send it.
File Format is export_MM-DD-YY.html
Id rather have it display the contents of the HTML in the email not the HTML file.
#!/usr/bin/env python3
import smtplib
import config
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
filename = 'name_of_file'
# Read a file and encode it into base64 format
fo = open(filename, "rb")
filecontent = fo.read()
encodedcontent = base64.b64encode(filecontent) # base64
filename = os.path.basename(filename)
# me == my email address
# you == recipient's email address
me = "From#example.com"
you = "TO#example.com"
subject = 'Test Subject v5'
# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = me
msg['To'] = you
# Create the body of the message (a plain-text and an HTML version).
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttps://www.python.org"
html = """\
**INSERT HTML FILE CONTENTS HERE...**
"""
# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')
# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part1)
msg.attach(part2)
# Send the message via local SMTP server.
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(config.EMAIL_ADDRESS, config.PASSWORD)
server.sendmail(me,you,msg.as_string())
server.quit()
So I think I got it working but im sure theres more code here than is needed (If anyone sees anything I can clean up?)
#!/usr/bin/env python3
import smtplib
import os
import config
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
raport_file = open('export.html','rb')
alert_msg = MIMEText(raport_file.read(),"html", "utf-8")
# me == my email address
# you == recipient's email address
me = "From#example.com"
you = "TO#example.com"
subject = 'Test Subject v5'
# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = subject
msg['From'] = me
msg['To'] = you
# Create the body of the message (a plain-text and an HTML version).
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttps://www.python.org"
html = """\
"""
# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')
# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part1)
msg.attach(part2)
# Send the message via local SMTP server.
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(config.EMAIL_ADDRESS, config.PASSWORD)
server.sendmail(me,you,alert_msg.as_string())
server.quit()
You're really close. Three changes:
Don't open the html file in binary mode. Read the file directly into the html string
report_file = open('export.html')
html = report_file.read()
remove the subsequent assignment to html var
html = """\
"""
send the msg object as constructed
server.sendmail(me, you, msg.as_string())
That worked for me. Also keep in mind that by default gmail settings may not allow your script to send mail. If so you'll need to update your settings to allow "insecure" (meaning non-Google) apps to send mail.

smtplib.SMTPRecipientsRefused: How to fix smtplib error in Python? [duplicate]

After much searching I couldn't find out how to use smtplib.sendmail to send to multiple recipients. The problem was every time the mail would be sent the mail headers would appear to contain multiple addresses, but in fact only the first recipient would receive the email.
The problem seems to be that the email.Message module expects something different than the smtplib.sendmail() function.
In short, to send to multiple recipients you should set the header to be a string of comma delimited email addresses. The sendmail() parameter to_addrs however should be a list of email addresses.
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
import smtplib
msg = MIMEMultipart()
msg["Subject"] = "Example"
msg["From"] = "me#example.com"
msg["To"] = "malcom#example.com,reynolds#example.com,firefly#example.com"
msg["Cc"] = "serenity#example.com,inara#example.com"
body = MIMEText("example email body")
msg.attach(body)
smtp = smtplib.SMTP("mailhost.example.com", 25)
smtp.sendmail(msg["From"], msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())
smtp.quit()
This really works, I spent a lot of time trying multiple variants.
import smtplib
from email.mime.text import MIMEText
s = smtplib.SMTP('smtp.uk.xensource.com')
s.set_debuglevel(1)
msg = MIMEText("""body""")
sender = 'me#example.com'
recipients = ['john.doe#example.com', 'john.smith#example.co.uk']
msg['Subject'] = "subject line"
msg['From'] = sender
msg['To'] = ", ".join(recipients)
s.sendmail(sender, recipients, msg.as_string())
The msg['To'] needs to be a string:
msg['To'] = "a#b.com, b#b.com, c#b.com"
While the recipients in sendmail(sender, recipients, message) needs to be a list:
sendmail("a#a.com", ["a#b.com", "b#b.com", "c#b.com"], "Howdy")
You need to understand the difference between the visible address of an email, and the delivery.
msg["To"] is essentially what is printed on the letter. It doesn't actually have any effect. Except that your email client, just like the regular post officer, will assume that this is who you want to send the email to.
The actual delivery however can work quite different. So you can drop the email (or a copy) into the post box of someone completely different.
There are various reasons for this. For example forwarding. The To: header field doesn't change on forwarding, however the email is dropped into a different mailbox.
The smtp.sendmail command now takes care of the actual delivery. email.Message is the contents of the letter only, not the delivery.
In low-level SMTP, you need to give the receipients one-by-one, which is why a list of adresses (not including names!) is the sensible API.
For the header, it can also contain for example the name, e.g. To: First Last <email#addr.tld>, Other User <other#mail.tld>. Your code example therefore is not recommended, as it will fail delivering this mail, since just by splitting it on , you still not not have the valid adresses!
It works for me.
import smtplib
from email.mime.text import MIMEText
s = smtplib.SMTP('smtp.uk.xensource.com')
s.set_debuglevel(1)
msg = MIMEText("""body""")
sender = 'me#example.com'
recipients = 'john.doe#example.com,john.smith#example.co.uk'
msg['Subject'] = "subject line"
msg['From'] = sender
msg['To'] = recipients
s.sendmail(sender, recipients.split(','), msg.as_string())
The solution below worked for me. It successfully sends an email to multiple recipients, including "CC" and "BCC."
toaddr = ['mailid_1','mailid_2']
cc = ['mailid_3','mailid_4']
bcc = ['mailid_5','mailid_6']
subject = 'Email from Python Code'
fromaddr = 'sender_mailid'
message = "\n !! Hello... !!"
msg['From'] = fromaddr
msg['To'] = ', '.join(toaddr)
msg['Cc'] = ', '.join(cc)
msg['Bcc'] = ', '.join(bcc)
msg['Subject'] = subject
s.sendmail(fromaddr, (toaddr+cc+bcc) , message)
So actually the problem is that SMTP.sendmail and email.MIMEText need two different things.
email.MIMEText sets up the "To:" header for the body of the e-mail. It is ONLY used for displaying a result to the human being at the other end, and like all e-mail headers, must be a single string. (Note that it does not actually have to have anything to do with the people who actually receive the message.)
SMTP.sendmail, on the other hand, sets up the "envelope" of the message for the SMTP protocol. It needs a Python list of strings, each of which has a single address.
So, what you need to do is COMBINE the two replies you received. Set msg['To'] to a single string, but pass the raw list to sendmail:
emails = ['a.com','b.com', 'c.com']
msg['To'] = ', '.join( emails )
....
s.sendmail( msg['From'], emails, msg.as_string())
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def sender(recipients):
body = 'Your email content here'
msg = MIMEMultipart()
msg['Subject'] = 'Email Subject'
msg['From'] = 'your.email#gmail.com'
msg['To'] = (', ').join(recipients.split(','))
msg.attach(MIMEText(body,'plain'))
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login('your.email#gmail.com', 'yourpassword')
server.send_message(msg)
server.quit()
if __name__ == '__main__':
sender('email_1#domain.com,email_2#domain.com')
It only worked for me with send_message function and using the join function in the list whith recipients, python 3.6.
I tried the below and it worked like a charm :)
rec_list = ['first#example.com', 'second#example.com']
rec = ', '.join(rec_list)
msg['To'] = rec
send_out = smtplib.SMTP('localhost')
send_out.sendmail(me, rec_list, msg.as_string())
I came up with this importable module function. It uses the gmail email server in this example. Its split into header and message so you can clearly see whats going on:
import smtplib
def send_alert(subject=""):
to = ['email#one.com', 'email2#another_email.com', 'a3rd#email.com']
gmail_user = 'me#gmail.com'
gmail_pwd = 'my_pass'
smtpserver = smtplib.SMTP("smtp.gmail.com", 587)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo
smtpserver.login(gmail_user, gmail_pwd)
header = 'To:' + ", ".join(to) + '\n' + 'From: ' + gmail_user + '\n' + 'Subject: ' + subject + '\n'
msg = header + '\n' + subject + '\n\n'
smtpserver.sendmail(gmail_user, to, msg)
smtpserver.close()
I use python 3.6 and the following code works for me
email_send = 'xxxxx#xxx.xxx,xxxx#xxx.xxx'
server.sendmail(email_user,email_send.split(','),text)
I figured this out a few months back and blogged about it. The summary is:
If you want to use smtplib to send email to multiple recipients, use email.Message.add_header('To', eachRecipientAsString) to add them, and then when you invoke the sendmail method, use email.Message.get_all('To') send the message to all of them. Ditto for Cc and Bcc recipients.
Well, the method in this asnwer method did not work for me. I don't know, maybe this is a Python3 (I am using the 3.4 version) or gmail related issue, but after some tries, the solution that worked for me, was the line
s.send_message(msg)
instead of
s.sendmail(sender, recipients, msg.as_string())
This is an old question. My main reason to post a new answer is to explain how to solve the problem with the modern email library in Python 3.6+ and how it differs from the old version; but first, let's recap what Anony-Mousse wrote in their answer from 2012.
SMTP doesn't care at all what's in the headers. The list of recipients you pass in to the sendmail method are what actually determine where the message will be delivered.
In SMTP parlance, this is called the message's envelope. On the protocol level, you connect to the server, then tell it who the message is from (MAIL FROM: SMTP verb) and who to send it to (RCPT TO:), then separately transmit the message itself (DATA) with headers and body as one oblique string blob.
The modern smtplib simplifies the Python side of this by providing a send_message method which actually sends to the recipients specified in the message's headers.
The modern email library provides an EmailMessage object which replaces all the various individual MIME types which you had to use in the past to assemble a message from smaller parts. You can add attachments without separately constructing them, and build various more complex multipart structures if you need to, but you normally don't have to. Just create a message and populate the parts you want.
Notice that the following is heavily commented; on the whole, the new EmailMessage API is more succinct and more versatile than the old API.
from email.message import EmailMessage
msg = EmailMessage()
# This example uses explicit strings to emphasize that
# that's what these header eventually get turned into
msg["From"] = "me#example.org"
msg["To"] = "main.recipient#example.net, other.main.recipient#example.org"
msg["Cc"] = "secondary#example.com, tertiary#example.eu"
msg["Bcc"] = "invisible#example.int, undisclosed#example.org.au"
msg["Subject"] = "Hello from the other side"
msg.set_content("This is the main text/plain message.")
# You can put an HTML body instead by adding a subtype string argument "html"
# msg.set_content("<p>This is the main text/html message.</p>", "html")
# You can add attachments of various types as you see fit;
# if there are no other parts, the message will be a simple
# text/plain or text/html, but Python will change it into a
# suitable multipart/related or etc if you add more parts
with open("image.png", "rb") as picture:
msg.add_attachment(picture.read(), maintype="image", subtype="png")
# Which port to use etc depends on the mail server.
# Traditionally, port 25 is SMTP, but modern SMTP MSA submission uses 587.
# Some servers accept encrypted SMTP_SSL on port 465.
# Here, we use SMTP instead of SMTP_SSL, but pivot to encrypted
# traffic with STARTTLS after the initial handshake.
with smtplib.SMTP("smtp.example.org", 587) as server:
# Some servers insist on this, others are more lenient ...
# It is technically required by ESMTP, so let's do it
# (If you use server.login() Python will perform an EHLO first
# if you haven't done that already, but let's cover all bases)
server.ehlo()
# Whether or not to use STARTTLS depends on the mail server
server.starttls()
# Bewilderingly, some servers require a second EHLO after STARTTLS!
server.ehlo()
# Login is the norm rather than the exception these days
# but if you are connecting to a local mail server which is
# not on the public internet, this might not be useful or even possible
server.login("me.myself#example.org", "xyzzy")
# Finally, send the message
server.send_message(msg)
The ultimate visibility of the Bcc: header depends on the mail server. If you want to be really sure that the recipients are not visible to each other, perhaps don't put a Bcc: header at all, and separately enumerate the envelope recipients in the envelope like you used to have to with sendmail (send_message lets you do that too, but you don't have to if you just want to send to the recipients named in the headers).
This obviously sends a single message to all recipients in one go. That is generally what you should be doing if you are sending the same message to a lot of people. However, if each message is unique, you will need to loop over the recipients and create and send a new message for each. (Merely wishing to put the recipient's name and address in the To: header is probably not enough to warrant sending many more messages than required, but of course, sometimes you have unique content for each recipient in the body, too.)
you can try this when you write the recpient emails on a text file
from email.mime.text import MIMEText
from email.header import Header
import smtplib
f = open('emails.txt', 'r').readlines()
for n in f:
emails = n.rstrip()
server = smtplib.SMTP('smtp.uk.xensource.com')
server.ehlo()
server.starttls()
body = "Test Email"
subject = "Test"
from = "me#example.com"
to = emails
msg = MIMEText(body,'plain','utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = Header(from, 'utf-8')
msg['To'] = Header(to, 'utf-8')
text = msg.as_string()
try:
server.send(from, emails, text)
print('Message Sent Succesfully')
except:
print('There Was An Error While Sending The Message')
There are a lot of answers on here that are technically or partially correct. After reading everyone's answers, I came up with this as a more solid/universal email function. I have confirmed it works and you can pass HTML or plain text for the body. Note that this code does not include attachment code:
import smtplib
import socket
# Import the email modules we'll need
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
#
# #param [String] email_list
# #param [String] subject_line
# #param [String] error_message
def sendEmailAlert(email_list="default#email.com", subject_line="Default Subject", error_message="Default Error Message"):
hostname = socket.gethostname()
# Create message
msg = MIMEMultipart()
msg['Subject'] = subject_line
msg['From'] = f'no-reply#{hostname}'
msg['To'] = email_list
msg.attach(MIMEText(error_message, 'html'))
# Send the message via SMTP server
s = smtplib.SMTP('localhost') # Change for remote mail server!
# Verbose debugging
s.set_debuglevel(2)
try:
s.sendmail(msg['From'], msg['To'].split(","), msg.as_string())
except Exception as e:
print(f'EMAIL ISSUE: {e}')
s.quit()
This can obviously be modified to use native Python logging. I am just providing a solid core function. I also can't stress this enough, sendmail() wants a List and NOT a String! Function is for Python3.6+
Try declaring a list variable with all recipients and cc_recipients as strings than looping over them, like this:
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
import smtplib
recipients = ["malcom#example.com","reynolds#example.com", "firefly#example.com"]
cc_recipients=["serenity#example.com", "inara#example.com"]
msg = MIMEMultipart()
msg["Subject"] = "Example"
msg["From"] = "me#example.com"
msg["To"] = ', '.join(recipients)
msg["Cc"] = ', '.join(cc_recipients)
body = MIMEText("example email body")
msg.attach(body)
smtp = smtplib.SMTP("mailhost.example.com", 25)
for recipient in recipients:
smtp.sendmail(msg["From"], recipient, msg.as_string())
for cc_recipient in cc_recipients:
smtp.sendmail(msg["From"], cc_recipient, msg.as_string())
smtp.quit()
For those who wish to send the message with only one 'To' header, the code below solves it. Ensure that your receivers variable is a list of strings.
# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = title
msg['From'] = f'support#{config("domain_base")}'
msg['To'] = "me"
message_content += f"""
<br /><br />
Regards,<br />
Company Name<br />
The {config("domain_base")} team
"""
body = MIMEText(message_content, 'html')
msg.attach(body)
try:
smtpObj = smtplib.SMTP('localhost')
for r in receivers:
del msg['To']
msg['To'] = r #"Customer /n" + r
smtpObj.sendmail(f"support#{config('domain_base')}", r, msg.as_string())
smtpObj.quit()
return {"message": "Successfully sent email"}
except smtplib.SMTPException:
return {"message": "Error: unable to send email"}
To send email to multiple recipients add receivers as list of email id.
receivers = ['user1#email.com', 'user2#email.com', 'user3#email.com']
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
smtp_server = 'smtp-example.com'
port = 26
sender = 'user#email.com'
debuglevel = 0
# add receivers as list of email id string
receivers = ['user1#email.com', 'user2#email.com', 'user3#email.com']
message = MIMEMultipart(
"mixed", None, [MIMEImage(img_data, 'png'), MIMEText(html,'html')])
message['Subject'] = "Token Data"
message['From'] = sender
message['To'] = ", ".join(receivers)
try:
server = smtplib.SMTP('smtp-example.com')
server.set_debuglevel(1)
server.sendmail(sender, receivers, message.as_string())
server.quit()
# print(response)
except BaseException:
print('Error: unable to send email')

How to send HTML text and attachment using boto3 send_email or send_raw_email?

How can I send an image attachment using boto3 SES send_email client?
I know that I can use send_raw_email to send an attachment but I need to send the message body with html data. If this is not possible, how can I send an email with html data using boto3.ses.send_raw_email() ?
After going through several sources, including other SO questions, blogs and Python documentation, I came up with the code below.
Allows for text and/or html emails and attachments.
Separated the MIME and boto3 parts, in case you want to re-use MIME for other purposes, like sending an email with a SMTP client, instead of boto3.
import os
import boto3
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
def create_multipart_message(
sender: str, recipients: list, title: str, text: str=None, html: str=None, attachments: list=None)\
-> MIMEMultipart:
"""
Creates a MIME multipart message object.
Uses only the Python `email` standard library.
Emails, both sender and recipients, can be just the email string or have the format 'The Name <the_email#host.com>'.
:param sender: The sender.
:param recipients: List of recipients. Needs to be a list, even if only one recipient.
:param title: The title of the email.
:param text: The text version of the email body (optional).
:param html: The html version of the email body (optional).
:param attachments: List of files to attach in the email.
:return: A `MIMEMultipart` to be used to send the email.
"""
multipart_content_subtype = 'alternative' if text and html else 'mixed'
msg = MIMEMultipart(multipart_content_subtype)
msg['Subject'] = title
msg['From'] = sender
msg['To'] = ', '.join(recipients)
# Record the MIME types of both parts - text/plain and text/html.
# According to RFC 2046, the last part of a multipart message, in this case the HTML message, is best and preferred.
if text:
part = MIMEText(text, 'plain')
msg.attach(part)
if html:
part = MIMEText(html, 'html')
msg.attach(part)
# Add attachments
for attachment in attachments or []:
with open(attachment, 'rb') as f:
part = MIMEApplication(f.read())
part.add_header('Content-Disposition', 'attachment', filename=os.path.basename(attachment))
msg.attach(part)
return msg
def send_mail(
sender: str, recipients: list, title: str, text: str=None, html: str=None, attachments: list=None) -> dict:
"""
Send email to recipients. Sends one mail to all recipients.
The sender needs to be a verified email in SES.
"""
msg = create_multipart_message(sender, recipients, title, text, html, attachments)
ses_client = boto3.client('ses') # Use your settings here
return ses_client.send_raw_email(
Source=sender,
Destinations=recipients,
RawMessage={'Data': msg.as_string()}
)
if __name__ == '__main__':
sender_ = 'The Sender <the_sender#email.com>'
recipients_ = ['Recipient One <recipient_1#email.com>', 'recipient_2#email.com']
title_ = 'Email title here'
text_ = 'The text version\nwith multiple lines.'
body_ = """<html><head></head><body><h1>A header 1</h1><br>Some text."""
attachments_ = ['/path/to/file1/filename1.txt', '/path/to/file2/filename2.txt']
response_ = send_mail(sender_, recipients_, title_, text_, body_, attachments_)
print(response_)
March 2019
Here is a copy-pasted solution from the UPDATED official documentation (https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-raw.html):
import os
import boto3
from botocore.exceptions import ClientError
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
# Replace sender#example.com with your "From" address.
# This address must be verified with Amazon SES.
SENDER = "Sender Name <sender#example.com>"
# Replace recipient#example.com with a "To" address. If your account
# is still in the sandbox, this address must be verified.
RECIPIENT = "recipient#example.com"
# Specify a configuration set. If you do not want to use a configuration
# set, comment the following variable, and the
# ConfigurationSetName=CONFIGURATION_SET argument below.
CONFIGURATION_SET = "ConfigSet"
# If necessary, replace us-west-2 with the AWS Region you're using for Amazon SES.
AWS_REGION = "us-west-2"
# The subject line for the email.
SUBJECT = "Customer service contact info"
# The full path to the file that will be attached to the email.
ATTACHMENT = "path/to/customers-to-contact.xlsx"
# The email body for recipients with non-HTML email clients.
BODY_TEXT = "Hello,\r\nPlease see the attached file for a list of customers to contact."
# The HTML body of the email.
BODY_HTML = """\
<html>
<head></head>
<body>
<h1>Hello!</h1>
<p>Please see the attached file for a list of customers to contact.</p>
</body>
</html>
"""
# The character encoding for the email.
CHARSET = "utf-8"
# Create a new SES resource and specify a region.
client = boto3.client('ses',region_name=AWS_REGION)
# Create a multipart/mixed parent container.
msg = MIMEMultipart('mixed')
# Add subject, from and to lines.
msg['Subject'] = SUBJECT
msg['From'] = SENDER
msg['To'] = RECIPIENT
# Create a multipart/alternative child container.
msg_body = MIMEMultipart('alternative')
# Encode the text and HTML content and set the character encoding. This step is
# necessary if you're sending a message with characters outside the ASCII range.
textpart = MIMEText(BODY_TEXT.encode(CHARSET), 'plain', CHARSET)
htmlpart = MIMEText(BODY_HTML.encode(CHARSET), 'html', CHARSET)
# Add the text and HTML parts to the child container.
msg_body.attach(textpart)
msg_body.attach(htmlpart)
# Define the attachment part and encode it using MIMEApplication.
att = MIMEApplication(open(ATTACHMENT, 'rb').read())
# Add a header to tell the email client to treat this part as an attachment,
# and to give the attachment a name.
att.add_header('Content-Disposition','attachment',filename=os.path.basename(ATTACHMENT))
# Attach the multipart/alternative child container to the multipart/mixed
# parent container.
msg.attach(msg_body)
# Add the attachment to the parent container.
msg.attach(att)
#print(msg)
try:
#Provide the contents of the email.
response = client.send_raw_email(
Source=SENDER,
Destinations=[
RECIPIENT
],
RawMessage={
'Data':msg.as_string(),
},
ConfigurationSetName=CONFIGURATION_SET
)
# Display an error if something goes wrong.
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
To expand upon #adkl's answer, Amazon's own example is using older Python way of handling emails and attachments. Nothing wrong with that, just that current documentation on those modules is not comprehensive and might be confusing for new users like me.
Here's simple example on forming message with CSV attachment.
from email.message import EmailMessage
def create_email_message(sender: str, recipients: list, title: str, text: str,
attachment: BytesIO, file_name: str) -> EmailMessage:
msg = EmailMessage()
msg["Subject"] = title
msg['From'] = sender
msg['To'] = ', '.join(recipients)
msg.set_content(text)
data = attachment.read()
msg.add_attachment(
data,
maintype="text",
subtype="csv",
filename=file_name
)
return msg
# Client init, attachment file creation here
message = create_email_message(...)
try:
ses.send_raw_email(
Source=sender,
Destinations=recipients,
RawMessage={'Data': message.as_string()}
)
except ClientError as e:
logger.exception(f"Cannot send email report to {recipients}: {e}")
else:
logger.info("Sent report successfully")
In this example I use BytesIO object as a source for attachment, but you can use any file-like object that supports read() method.
Shameless copy example from "HOW TO SEND HTML MAILS USING AMAZON SES "
This is how a typical email data content looks like.
message_dict = { 'Data':
'From: ' + mail_sender + '\n'
'To: ' + mail_receivers_list + '\n'
'Subject: ' + mail_subject + '\n'
'MIME-Version: 1.0\n'
'Content-Type: text/html;\n\n' +
mail_content}
If you want to send attachment and HTML text using boto3.ses.send_raw_email, you just need use above message dict and pass. (just put your html text under the mail_content)
response = client.send_raw_email(
Destinations=[
],
FromArn='',
RawMessage=message_dict,
ReturnPathArn='',
Source='',
SourceArn='',
)
In fact, the raw attachment header should works in both send_email() and send_raw_email() . Except in send_mail, you should put the attachment inside Text, not html.
This worked for me to send an attachment:
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import boto.ses
AWS_ACCESS_KEY = 'HEREYOURACCESSKEY'
AWS_SECRET_KEY = 'HEREYOURSECRETKEY'
class Email(object):
def __init__(self, to, subject):
self.to = to
self.subject = subject
self.text = None
self.attachment = None
def text(self, text):
self.text = text
def add_attachment(self, attachment):
self.attachment = attachment
def send(self, from_addr=None, file_name = None):
connection = boto.ses.connect_to_region(
'us-east-1',
aws_access_key_id=AWS_ACCESS_KEY,
aws_secret_access_key=AWS_SECRET_KEY
)
msg = MIMEMultipart()
msg['Subject'] = self.subject
msg['From'] = from_addr
msg['To'] = self.to
part = MIMEApplication(self.attachment)
part.add_header('Content-Disposition', 'attachment', filename=file_name)
part.add_header('Content-Type', 'application/vnd.ms-excel; charset=UTF-8')
msg.attach(part)
# the message body
part = MIMEText(self.text)
msg.attach(part)
return connection.send_raw_email(msg.as_string(),source=from_addr,destinations=self.to)
if __name__ == "__main__":
email = Email(to='toMail#gmail.com', subject='Your subject!')
email.text('This is a text body.')
#you could use StringIO.StringIO() to get the file value
email.add_attachment(yourFileValue)
email.send(from_addr='from#mail.com',file_name="yourFile.txt")
Here's the class I ended up using. Drop into a Lambda file and use it.
Accepts a list of filenames for attachments. Sends HTML email. Changes \n for <br />
I saved the below class [1] as emailer.py and use as:
from emailer import Emailer
def lambda_handler(event, context):
# ...
emailer = Emailer()
emailer.send(
to=email_recipients_list,
subject=subject_string,
fromx=from_address_string,
body=email_body_string,
attachments=attachments_list
)
[1]
import boto3
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
class Emailer(object):
""" send email with attachments """
def send(self, to, subject, fromx, body=None, content=None, attachments=None):
""" sends email with attachments
Parameters:
* to (list or comma separated string of addresses): recipient(s) address
* fromx (string): from address of email
* body (string, optional): Body of email ('\n' are converted to '< br/>')
* content (string, optional): Body of email specified as filename
* attachments (list, optional): list of paths of files to attach
"""
if attachments is None:
attachments = []
self.to = to
self.subject = subject
self.fromx = fromx
self.attachment = None
self.body = body
self.content = content
self.attachments = attachments
if type(self.to) is list:
self.to = ",".join(self.to)
message = MIMEMultipart()
message['Subject'] = self.subject
message['From'] = self.fromx
message['To'] = self.to
if self.content and os.path.isfile(self.content):
part = MIMEText(open(str(self.content)).read().replace("\n", "<br />"), "html")
message.attach(part)
elif self.body:
part = MIMEText(self.body.replace("\\n", "<br />").replace("\n", "<br />"), "html")
message.attach(part)
for attachment in self.attachments:
part = MIMEApplication(open(attachment, 'rb').read())
part.add_header('Content-Disposition', 'attachment', filename=attachment.split("/")[-1])
message.attach(part)
ses = boto3.client('ses', region_name='us-east-1')
response = ses.send_raw_email(
Source=message['From'],
Destinations=message['To'].split(","),
RawMessage={
'Data': message.as_string()
}
)
This can also be implemented using python version 2.7.x
Following is the working code for this--
[Note - The 'sender' and 'recipients' that you add must be verified in AWS SES. OR SES must be moved from 'sandbox' state to 'Production' state
import os
import boto3
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
def create_multipart_message(email_metadata):
sender = email_metadata['sender_']
recipients = email_metadata['recipients_']
title = email_metadata['title_']
text = email_metadata['text_']
html = email_metadata['body_']
attachments = email_metadata['attachments_']
multipart_content_subtype = 'alternative' if text and html else 'mixed'
msg = MIMEMultipart(multipart_content_subtype)
msg['Subject'] = title
msg['From'] = sender
msg['To'] = ', '.join(recipients)
# Record the MIME types of both parts - text/plain and text/html.
# According to RFC 2046, the last part of a multipart message, in this case the HTML message, is best and preferred.
if text:
part = MIMEText(text, 'plain')
msg.attach(part)
if html:
part = MIMEText(html, 'html')
msg.attach(part)
# Add attachments
for attachment in attachments or []:
with open(attachment, 'rb') as f:
part = MIMEApplication(f.read())
part.add_header('Content-Disposition', 'attachment', filename=os.path.basename(attachment))
msg.attach(part)
return msg
def send_mail(email_metadata):
#sender: str, recipients: list, title: str, text: str=None, html: str=None, attachments: list=None) -> dict:
"""
Send email to recipients. Sends one mail to all recipients.
The sender needs to be a verified email in SES.
"""
msg = create_multipart_message(email_metadata)
ses_client = boto3.client('ses') # Use your settings here
return ses_client.send_raw_email(
Source=email_metadata['sender_'],
Destinations=email_metadata['recipients_'],
RawMessage={'Data': msg.as_string()}
)
if __name__ == '__main__':
email_metadata = {
"sender_" : "The Sender <the_sender#email.com>",
"recipients_" : ['Recipient One <recipient_1#email.com>','Recipient two <recipient_2#email.com>'],
"title_" : "Email title here",
"text_" : "The text version\nwith multiple lines.",
"body_" : "<html><head></head><body><h1>A header 1</h1><br>Some text.",
"attachments_" : ['/path/to/file1/filename1.txt','/path/to/file2/filename2.txt']
}
response_ = send_mail(email_metadata)
print(response_)

embed HTML table in email via python [duplicate]

How to send HTML content in email using Python? I can send simple texts.
From Python v2.7.14 documentation - 18.1.11. email: Examples:
Here’s an example of how to create an HTML message with an alternative plain text version:
#! /usr/bin/python
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# me == my email address
# you == recipient's email address
me = "my#email.com"
you = "your#email.com"
# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = "Link"
msg['From'] = me
msg['To'] = you
# Create the body of the message (a plain-text and an HTML version).
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttp://www.python.org"
html = """\
<html>
<head></head>
<body>
<p>Hi!<br>
How are you?<br>
Here is the link you wanted.
</p>
</body>
</html>
"""
# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')
# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part1)
msg.attach(part2)
# Send the message via local SMTP server.
s = smtplib.SMTP('localhost')
# sendmail function takes 3 arguments: sender's address, recipient's address
# and message to send - here it is sent as one string.
s.sendmail(me, you, msg.as_string())
s.quit()
Here is a Gmail implementation of the accepted answer:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# me == my email address
# you == recipient's email address
me = "my#email.com"
you = "your#email.com"
# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = "Link"
msg['From'] = me
msg['To'] = you
# Create the body of the message (a plain-text and an HTML version).
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttp://www.python.org"
html = """\
<html>
<head></head>
<body>
<p>Hi!<br>
How are you?<br>
Here is the link you wanted.
</p>
</body>
</html>
"""
# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')
# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part1)
msg.attach(part2)
# Send the message via local SMTP server.
mail = smtplib.SMTP('smtp.gmail.com', 587)
mail.ehlo()
mail.starttls()
mail.login('userName', 'password')
mail.sendmail(me, you, msg.as_string())
mail.quit()
You might try using my mailer module.
from mailer import Mailer
from mailer import Message
message = Message(From="me#example.com",
To="you#example.com")
message.Subject = "An HTML Email"
message.Html = """<p>Hi!<br>
How are you?<br>
Here is the link you wanted.</p>"""
sender = Mailer('smtp.example.com')
sender.send(message)
Here is a simple way to send an HTML email, just by specifying the Content-Type header as 'text/html':
import email.message
import smtplib
msg = email.message.Message()
msg['Subject'] = 'foo'
msg['From'] = 'sender#test.com'
msg['To'] = 'recipient#test.com'
msg.add_header('Content-Type','text/html')
msg.set_payload('Body of <b>message</b>')
# Send the message via local SMTP server.
s = smtplib.SMTP('localhost')
s.starttls()
s.login(email_login,
email_passwd)
s.sendmail(msg['From'], [msg['To']], msg.as_string())
s.quit()
for python3, improve #taltman 's answer:
use email.message.EmailMessage instead of email.message.Message to construct email.
use email.set_content func, assign subtype='html' argument. instead of low level func set_payload and add header manually.
use SMTP.send_message func instead of SMTP.sendmail func to send email.
use with block to auto close connection.
from email.message import EmailMessage
from smtplib import SMTP
# construct email
email = EmailMessage()
email['Subject'] = 'foo'
email['From'] = 'sender#test.com'
email['To'] = 'recipient#test.com'
email.set_content('<font color="red">red color text</font>', subtype='html')
# Send the message via local SMTP server.
with smtplib.SMTP('localhost') as s:
s.login('foo_user', 'bar_password')
s.send_message(email)
Here's sample code. This is inspired from code found on the Python Cookbook site (can't find the exact link)
def createhtmlmail (html, text, subject, fromEmail):
"""Create a mime-message that will render HTML in popular
MUAs, text in better ones"""
import MimeWriter
import mimetools
import cStringIO
out = cStringIO.StringIO() # output buffer for our message
htmlin = cStringIO.StringIO(html)
txtin = cStringIO.StringIO(text)
writer = MimeWriter.MimeWriter(out)
#
# set up some basic headers... we put subject here
# because smtplib.sendmail expects it to be in the
# message body
#
writer.addheader("From", fromEmail)
writer.addheader("Subject", subject)
writer.addheader("MIME-Version", "1.0")
#
# start the multipart section of the message
# multipart/alternative seems to work better
# on some MUAs than multipart/mixed
#
writer.startmultipartbody("alternative")
writer.flushheaders()
#
# the plain text section
#
subpart = writer.nextpart()
subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
pout = subpart.startbody("text/plain", [("charset", 'us-ascii')])
mimetools.encode(txtin, pout, 'quoted-printable')
txtin.close()
#
# start the html subpart of the message
#
subpart = writer.nextpart()
subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
#
# returns us a file-ish object we can write to
#
pout = subpart.startbody("text/html", [("charset", 'us-ascii')])
mimetools.encode(htmlin, pout, 'quoted-printable')
htmlin.close()
#
# Now that we're done, close our writer and
# return the message body
#
writer.lastpart()
msg = out.getvalue()
out.close()
print msg
return msg
if __name__=="__main__":
import smtplib
html = 'html version'
text = 'TEST VERSION'
subject = "BACKUP REPORT"
message = createhtmlmail(html, text, subject, 'From Host <sender#host.com>')
server = smtplib.SMTP("smtp_server_address","smtp_port")
server.login('username', 'password')
server.sendmail('sender#host.com', 'target#otherhost.com', message)
server.quit()
Actually, yagmail took a bit different approach.
It will by default send HTML, with automatic fallback for incapable email-readers. It is not the 17th century anymore.
Of course, it can be overridden, but here goes:
import yagmail
yag = yagmail.SMTP("me#example.com", "mypassword")
html_msg = """<p>Hi!<br>
How are you?<br>
Here is the link you wanted.</p>"""
yag.send("to#example.com", "the subject", html_msg)
For installation instructions and many more great features, have a look at the github.
Here's a working example to send plain text and HTML emails from Python using smtplib along with the CC and BCC options.
https://varunver.wordpress.com/2017/01/26/python-smtplib-send-plaintext-and-html-emails/
#!/usr/bin/env python
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def send_mail(params, type_):
email_subject = params['email_subject']
email_from = "from_email#domain.com"
email_to = params['email_to']
email_cc = params.get('email_cc')
email_bcc = params.get('email_bcc')
email_body = params['email_body']
msg = MIMEMultipart('alternative')
msg['To'] = email_to
msg['CC'] = email_cc
msg['Subject'] = email_subject
mt_html = MIMEText(email_body, type_)
msg.attach(mt_html)
server = smtplib.SMTP('YOUR_MAIL_SERVER.DOMAIN.COM')
server.set_debuglevel(1)
toaddrs = [email_to] + [email_cc] + [email_bcc]
server.sendmail(email_from, toaddrs, msg.as_string())
server.quit()
# Calling the mailer functions
params = {
'email_to': 'to_email#domain.com',
'email_cc': 'cc_email#domain.com',
'email_bcc': 'bcc_email#domain.com',
'email_subject': 'Test message from python library',
'email_body': '<h1>Hello World</h1>'
}
for t in ['plain', 'html']:
send_mail(params, t)
Here is my answer for AWS using boto3
subject = "Hello"
html = "<b>Hello Consumer</b>"
client = boto3.client('ses', region_name='us-east-1', aws_access_key_id="your_key",
aws_secret_access_key="your_secret")
client.send_email(
Source='ACME <do-not-reply#acme.com>',
Destination={'ToAddresses': [email]},
Message={
'Subject': {'Data': subject},
'Body': {
'Html': {'Data': html}
}
}
Simplest solution for sending email from Organizational account in Office 365:
from O365 import Message
html_template = """
<html>
<head>
<title></title>
</head>
<body>
{}
</body>
</html>
"""
final_html_data = html_template.format(df.to_html(index=False))
o365_auth = ('sender_username#company_email.com','Password')
m = Message(auth=o365_auth)
m.setRecipients('receiver_username#company_email.com')
m.setSubject('Weekly report')
m.setBodyHTML(final_html_data)
m.sendMessage()
here df is a dataframe converted to html Table, which is being injected to html_template
I may be late in providing an answer here, but the Question asked a way to send HTML emails. Using a dedicated module like "email" is okay, but we can achieve the same results without using any new module. It all boils down to the Gmail Protocol.
Below is my simple sample code for sending HTML mail only by using "smtplib" and nothing else.
```
import smtplib
FROM = "....#gmail.com"
TO = "another....#gmail.com"
SUBJECT= "Subject"
PWD = "thesecretkey"
TEXT="""
<h1>Hello</h1>
""" #Your Message (Even Supports HTML Directly)
message = f"Subject: {SUBJECT}\nFrom: {FROM}\nTo: {TO}\nContent-Type: text/html\n\n{TEXT}" #This is where the stuff happens
try:
server=smtplib.SMTP("smtp.gmail.com",587)
server.ehlo()
server.starttls()
server.login(FROM,PWD)
server.sendmail(FROM,TO,message)
server.close()
print("Successfully sent the mail.")
except Exception as e:
print("Failed to send the mail..", e)
```
In case you want something simpler:
from redmail import EmailSender
email = EmailSender(host="smtp.myhost.com", port=1)
email.send(
subject="Example email",
sender="me#example.com",
receivers=["you#example.com"],
html="<h1>Hi, this is HTML body</h1>"
)
Pip install Red Mail from PyPI:
pip install redmail
Red Mail has most likely all you need from sending emails and it has a lot of features including:
Attachments
Templating (with Jinja)
Embedded images
Prettified tables
Send as cc or bcc
Gmail preconfigured
Documentation: https://red-mail.readthedocs.io/en/latest/index.html
Source code: https://github.com/Miksus/red-mail

How to send email to multiple recipients using python smtplib?

After much searching I couldn't find out how to use smtplib.sendmail to send to multiple recipients. The problem was every time the mail would be sent the mail headers would appear to contain multiple addresses, but in fact only the first recipient would receive the email.
The problem seems to be that the email.Message module expects something different than the smtplib.sendmail() function.
In short, to send to multiple recipients you should set the header to be a string of comma delimited email addresses. The sendmail() parameter to_addrs however should be a list of email addresses.
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
import smtplib
msg = MIMEMultipart()
msg["Subject"] = "Example"
msg["From"] = "me#example.com"
msg["To"] = "malcom#example.com,reynolds#example.com,firefly#example.com"
msg["Cc"] = "serenity#example.com,inara#example.com"
body = MIMEText("example email body")
msg.attach(body)
smtp = smtplib.SMTP("mailhost.example.com", 25)
smtp.sendmail(msg["From"], msg["To"].split(",") + msg["Cc"].split(","), msg.as_string())
smtp.quit()
This really works, I spent a lot of time trying multiple variants.
import smtplib
from email.mime.text import MIMEText
s = smtplib.SMTP('smtp.uk.xensource.com')
s.set_debuglevel(1)
msg = MIMEText("""body""")
sender = 'me#example.com'
recipients = ['john.doe#example.com', 'john.smith#example.co.uk']
msg['Subject'] = "subject line"
msg['From'] = sender
msg['To'] = ", ".join(recipients)
s.sendmail(sender, recipients, msg.as_string())
The msg['To'] needs to be a string:
msg['To'] = "a#b.com, b#b.com, c#b.com"
While the recipients in sendmail(sender, recipients, message) needs to be a list:
sendmail("a#a.com", ["a#b.com", "b#b.com", "c#b.com"], "Howdy")
You need to understand the difference between the visible address of an email, and the delivery.
msg["To"] is essentially what is printed on the letter. It doesn't actually have any effect. Except that your email client, just like the regular post officer, will assume that this is who you want to send the email to.
The actual delivery however can work quite different. So you can drop the email (or a copy) into the post box of someone completely different.
There are various reasons for this. For example forwarding. The To: header field doesn't change on forwarding, however the email is dropped into a different mailbox.
The smtp.sendmail command now takes care of the actual delivery. email.Message is the contents of the letter only, not the delivery.
In low-level SMTP, you need to give the receipients one-by-one, which is why a list of adresses (not including names!) is the sensible API.
For the header, it can also contain for example the name, e.g. To: First Last <email#addr.tld>, Other User <other#mail.tld>. Your code example therefore is not recommended, as it will fail delivering this mail, since just by splitting it on , you still not not have the valid adresses!
It works for me.
import smtplib
from email.mime.text import MIMEText
s = smtplib.SMTP('smtp.uk.xensource.com')
s.set_debuglevel(1)
msg = MIMEText("""body""")
sender = 'me#example.com'
recipients = 'john.doe#example.com,john.smith#example.co.uk'
msg['Subject'] = "subject line"
msg['From'] = sender
msg['To'] = recipients
s.sendmail(sender, recipients.split(','), msg.as_string())
The solution below worked for me. It successfully sends an email to multiple recipients, including "CC" and "BCC."
toaddr = ['mailid_1','mailid_2']
cc = ['mailid_3','mailid_4']
bcc = ['mailid_5','mailid_6']
subject = 'Email from Python Code'
fromaddr = 'sender_mailid'
message = "\n !! Hello... !!"
msg['From'] = fromaddr
msg['To'] = ', '.join(toaddr)
msg['Cc'] = ', '.join(cc)
msg['Bcc'] = ', '.join(bcc)
msg['Subject'] = subject
s.sendmail(fromaddr, (toaddr+cc+bcc) , message)
So actually the problem is that SMTP.sendmail and email.MIMEText need two different things.
email.MIMEText sets up the "To:" header for the body of the e-mail. It is ONLY used for displaying a result to the human being at the other end, and like all e-mail headers, must be a single string. (Note that it does not actually have to have anything to do with the people who actually receive the message.)
SMTP.sendmail, on the other hand, sets up the "envelope" of the message for the SMTP protocol. It needs a Python list of strings, each of which has a single address.
So, what you need to do is COMBINE the two replies you received. Set msg['To'] to a single string, but pass the raw list to sendmail:
emails = ['a.com','b.com', 'c.com']
msg['To'] = ', '.join( emails )
....
s.sendmail( msg['From'], emails, msg.as_string())
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def sender(recipients):
body = 'Your email content here'
msg = MIMEMultipart()
msg['Subject'] = 'Email Subject'
msg['From'] = 'your.email#gmail.com'
msg['To'] = (', ').join(recipients.split(','))
msg.attach(MIMEText(body,'plain'))
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login('your.email#gmail.com', 'yourpassword')
server.send_message(msg)
server.quit()
if __name__ == '__main__':
sender('email_1#domain.com,email_2#domain.com')
It only worked for me with send_message function and using the join function in the list whith recipients, python 3.6.
I tried the below and it worked like a charm :)
rec_list = ['first#example.com', 'second#example.com']
rec = ', '.join(rec_list)
msg['To'] = rec
send_out = smtplib.SMTP('localhost')
send_out.sendmail(me, rec_list, msg.as_string())
I came up with this importable module function. It uses the gmail email server in this example. Its split into header and message so you can clearly see whats going on:
import smtplib
def send_alert(subject=""):
to = ['email#one.com', 'email2#another_email.com', 'a3rd#email.com']
gmail_user = 'me#gmail.com'
gmail_pwd = 'my_pass'
smtpserver = smtplib.SMTP("smtp.gmail.com", 587)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo
smtpserver.login(gmail_user, gmail_pwd)
header = 'To:' + ", ".join(to) + '\n' + 'From: ' + gmail_user + '\n' + 'Subject: ' + subject + '\n'
msg = header + '\n' + subject + '\n\n'
smtpserver.sendmail(gmail_user, to, msg)
smtpserver.close()
I use python 3.6 and the following code works for me
email_send = 'xxxxx#xxx.xxx,xxxx#xxx.xxx'
server.sendmail(email_user,email_send.split(','),text)
I figured this out a few months back and blogged about it. The summary is:
If you want to use smtplib to send email to multiple recipients, use email.Message.add_header('To', eachRecipientAsString) to add them, and then when you invoke the sendmail method, use email.Message.get_all('To') send the message to all of them. Ditto for Cc and Bcc recipients.
Well, the method in this asnwer method did not work for me. I don't know, maybe this is a Python3 (I am using the 3.4 version) or gmail related issue, but after some tries, the solution that worked for me, was the line
s.send_message(msg)
instead of
s.sendmail(sender, recipients, msg.as_string())
This is an old question. My main reason to post a new answer is to explain how to solve the problem with the modern email library in Python 3.6+ and how it differs from the old version; but first, let's recap what Anony-Mousse wrote in their answer from 2012.
SMTP doesn't care at all what's in the headers. The list of recipients you pass in to the sendmail method are what actually determine where the message will be delivered.
In SMTP parlance, this is called the message's envelope. On the protocol level, you connect to the server, then tell it who the message is from (MAIL FROM: SMTP verb) and who to send it to (RCPT TO:), then separately transmit the message itself (DATA) with headers and body as one oblique string blob.
The modern smtplib simplifies the Python side of this by providing a send_message method which actually sends to the recipients specified in the message's headers.
The modern email library provides an EmailMessage object which replaces all the various individual MIME types which you had to use in the past to assemble a message from smaller parts. You can add attachments without separately constructing them, and build various more complex multipart structures if you need to, but you normally don't have to. Just create a message and populate the parts you want.
Notice that the following is heavily commented; on the whole, the new EmailMessage API is more succinct and more versatile than the old API.
from email.message import EmailMessage
msg = EmailMessage()
# This example uses explicit strings to emphasize that
# that's what these header eventually get turned into
msg["From"] = "me#example.org"
msg["To"] = "main.recipient#example.net, other.main.recipient#example.org"
msg["Cc"] = "secondary#example.com, tertiary#example.eu"
msg["Bcc"] = "invisible#example.int, undisclosed#example.org.au"
msg["Subject"] = "Hello from the other side"
msg.set_content("This is the main text/plain message.")
# You can put an HTML body instead by adding a subtype string argument "html"
# msg.set_content("<p>This is the main text/html message.</p>", "html")
# You can add attachments of various types as you see fit;
# if there are no other parts, the message will be a simple
# text/plain or text/html, but Python will change it into a
# suitable multipart/related or etc if you add more parts
with open("image.png", "rb") as picture:
msg.add_attachment(picture.read(), maintype="image", subtype="png")
# Which port to use etc depends on the mail server.
# Traditionally, port 25 is SMTP, but modern SMTP MSA submission uses 587.
# Some servers accept encrypted SMTP_SSL on port 465.
# Here, we use SMTP instead of SMTP_SSL, but pivot to encrypted
# traffic with STARTTLS after the initial handshake.
with smtplib.SMTP("smtp.example.org", 587) as server:
# Some servers insist on this, others are more lenient ...
# It is technically required by ESMTP, so let's do it
# (If you use server.login() Python will perform an EHLO first
# if you haven't done that already, but let's cover all bases)
server.ehlo()
# Whether or not to use STARTTLS depends on the mail server
server.starttls()
# Bewilderingly, some servers require a second EHLO after STARTTLS!
server.ehlo()
# Login is the norm rather than the exception these days
# but if you are connecting to a local mail server which is
# not on the public internet, this might not be useful or even possible
server.login("me.myself#example.org", "xyzzy")
# Finally, send the message
server.send_message(msg)
The ultimate visibility of the Bcc: header depends on the mail server. If you want to be really sure that the recipients are not visible to each other, perhaps don't put a Bcc: header at all, and separately enumerate the envelope recipients in the envelope like you used to have to with sendmail (send_message lets you do that too, but you don't have to if you just want to send to the recipients named in the headers).
This obviously sends a single message to all recipients in one go. That is generally what you should be doing if you are sending the same message to a lot of people. However, if each message is unique, you will need to loop over the recipients and create and send a new message for each. (Merely wishing to put the recipient's name and address in the To: header is probably not enough to warrant sending many more messages than required, but of course, sometimes you have unique content for each recipient in the body, too.)
you can try this when you write the recpient emails on a text file
from email.mime.text import MIMEText
from email.header import Header
import smtplib
f = open('emails.txt', 'r').readlines()
for n in f:
emails = n.rstrip()
server = smtplib.SMTP('smtp.uk.xensource.com')
server.ehlo()
server.starttls()
body = "Test Email"
subject = "Test"
from = "me#example.com"
to = emails
msg = MIMEText(body,'plain','utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = Header(from, 'utf-8')
msg['To'] = Header(to, 'utf-8')
text = msg.as_string()
try:
server.send(from, emails, text)
print('Message Sent Succesfully')
except:
print('There Was An Error While Sending The Message')
There are a lot of answers on here that are technically or partially correct. After reading everyone's answers, I came up with this as a more solid/universal email function. I have confirmed it works and you can pass HTML or plain text for the body. Note that this code does not include attachment code:
import smtplib
import socket
# Import the email modules we'll need
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
#
# #param [String] email_list
# #param [String] subject_line
# #param [String] error_message
def sendEmailAlert(email_list="default#email.com", subject_line="Default Subject", error_message="Default Error Message"):
hostname = socket.gethostname()
# Create message
msg = MIMEMultipart()
msg['Subject'] = subject_line
msg['From'] = f'no-reply#{hostname}'
msg['To'] = email_list
msg.attach(MIMEText(error_message, 'html'))
# Send the message via SMTP server
s = smtplib.SMTP('localhost') # Change for remote mail server!
# Verbose debugging
s.set_debuglevel(2)
try:
s.sendmail(msg['From'], msg['To'].split(","), msg.as_string())
except Exception as e:
print(f'EMAIL ISSUE: {e}')
s.quit()
This can obviously be modified to use native Python logging. I am just providing a solid core function. I also can't stress this enough, sendmail() wants a List and NOT a String! Function is for Python3.6+
Try declaring a list variable with all recipients and cc_recipients as strings than looping over them, like this:
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
import smtplib
recipients = ["malcom#example.com","reynolds#example.com", "firefly#example.com"]
cc_recipients=["serenity#example.com", "inara#example.com"]
msg = MIMEMultipart()
msg["Subject"] = "Example"
msg["From"] = "me#example.com"
msg["To"] = ', '.join(recipients)
msg["Cc"] = ', '.join(cc_recipients)
body = MIMEText("example email body")
msg.attach(body)
smtp = smtplib.SMTP("mailhost.example.com", 25)
for recipient in recipients:
smtp.sendmail(msg["From"], recipient, msg.as_string())
for cc_recipient in cc_recipients:
smtp.sendmail(msg["From"], cc_recipient, msg.as_string())
smtp.quit()
For those who wish to send the message with only one 'To' header, the code below solves it. Ensure that your receivers variable is a list of strings.
# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = title
msg['From'] = f'support#{config("domain_base")}'
msg['To'] = "me"
message_content += f"""
<br /><br />
Regards,<br />
Company Name<br />
The {config("domain_base")} team
"""
body = MIMEText(message_content, 'html')
msg.attach(body)
try:
smtpObj = smtplib.SMTP('localhost')
for r in receivers:
del msg['To']
msg['To'] = r #"Customer /n" + r
smtpObj.sendmail(f"support#{config('domain_base')}", r, msg.as_string())
smtpObj.quit()
return {"message": "Successfully sent email"}
except smtplib.SMTPException:
return {"message": "Error: unable to send email"}
To send email to multiple recipients add receivers as list of email id.
receivers = ['user1#email.com', 'user2#email.com', 'user3#email.com']
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
smtp_server = 'smtp-example.com'
port = 26
sender = 'user#email.com'
debuglevel = 0
# add receivers as list of email id string
receivers = ['user1#email.com', 'user2#email.com', 'user3#email.com']
message = MIMEMultipart(
"mixed", None, [MIMEImage(img_data, 'png'), MIMEText(html,'html')])
message['Subject'] = "Token Data"
message['From'] = sender
message['To'] = ", ".join(receivers)
try:
server = smtplib.SMTP('smtp-example.com')
server.set_debuglevel(1)
server.sendmail(sender, receivers, message.as_string())
server.quit()
# print(response)
except BaseException:
print('Error: unable to send email')

Categories

Resources