I have written a script that writes a message to a text file and also sends it as an email.
Everything goes well, except the email finally appears to be all in one line.
I add line breaks by \n and it works for the text file but not for the email.
Do you know what could be the possible reason?
Here's my code:
import smtplib, sys
import traceback
def send_error(sender, recipient, headers, body):
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
session = smtplib.SMTP('smtp.gmail.com', 587)
session.ehlo()
session.starttls()
session.ehlo
session.login(sender, 'my password')
send_it = session.sendmail(sender, recipient, headers + "\r\n\r\n" + body)
session.quit()
return send_it
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
sender = 'sender_id#gmail.com'
recipient = 'recipient_id#yahoo.com'
subject = 'report'
body = "Dear Student, \n Please send your report\n Thank you for your attention"
open('student.txt', 'w').write(body)
headers = ["From: " + sender,
"Subject: " + subject,
"To: " + recipient,
"MIME-Version: 1.0",
"Content-Type: text/html"]
headers = "\r\n".join(headers)
send_error(sender, recipient, headers, body)
Unfortunately for us all, not every type of program or application uses the same standardization that python does.
Looking at your question i notice your header is: "Content-Type: text/html"
Which means you need to use HTML style tags for your new-lines, these are called line-breaks. <br>
Your text should be:
"Dear Student, <br> Please send your report<br> Thank you for your attention"
If you would rather use character type new-lines, you must change the header to read: "Content-Type: text/plain"
You would still have to change the new-line character from a single \n to the double \r\n which is used in email.
Your text would be:
"Dear Student, \r\n Please send your report\r\n Thank you for your attention"
You have your message body declared to have HTML content ("Content-Type: text/html"). The HTML code for line break is <br>. You should either change your content type to text/plain or use the HTML markup for line breaks instead of plain \n as the latter gets ignored when rendering a HTML document.
As a side note, also have a look at the email package. There are some classes that can simplify the definition of E-Mail messages for you (with examples).
For example you could try (untested):
import smtplib
from email.mime.text import MIMEText
# define content
recipients = ["recipient_id#yahoo.com"]
sender = "sender_id#gmail.com"
subject = "report reminder"
body = """
Dear Student,
Please send your report
Thank you for your attention
"""
# make up message
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = ", ".join(recipients)
# sending
session = smtplib.SMTP('smtp.gmail.com', 587)
session.starttls()
session.login(sender, 'my password')
send_it = session.sendmail(sender, recipients, msg.as_string())
session.quit()
In my case '\r\n' didn't work, but '\r\r\n' did. So my code was:
from email.mime.text import MIMEText
body = 'Dear Student,\r\r\nPlease send your report\r\r\nThank you for your attention'
msg.attach(MIMEText(body, 'plain'))
The message is written in multiple lines and is displayed correctly in Outlook.
I've also run into this as well. I had found a little bit of white space at the end of the line was enough for my SMTP service to recognize the new line
body = 'value of variable x = ' + myVarX + " \r\n" \
+ 'value of variable y = ' + myVarY
I believe this to be more of a SMTP issue, rather than a Python issue, which may explain the range in solutions in this thread
Setting the content-type header to Content-Type: text/plain (with \r\n at the end) allowed me to send multi-line plain-text emails.
Outlook will remove line feeds from plain text it believes are extras. https://support.microsoft.com/en-us/kb/287816
You can try below update to make the lines look like bullets. That worked for me.
body = "Dear Student, \n- Please send your report\n- Thank you for your attention"
I ran into this issue as well and this thread was helpful in determining why it was happening in the first place. I was at a lost, because I knew my code was correct. I tried a few things, but what worked for me was adding a \t\n for each line in the body.
from email.mime.text import MIMEText
lines = ['line1', 'line2', 'line3']
body = '\t\n'.join(lines)
msg = MIMEText(body)
please check the version of python.
for version 3.11.1,
you should use '\r\n' for line break
reference the official doc https://docs.python.org/3/library/smtplib.html#:~:text=using%20BytesGenerator%20with-,%5Cr%5Cn%20as%20the%20linesep,-%2C%20and%20calls%20sendmail
Adding \n\n works for me
body = """"""
body += "This is message1\n\n"
body += "This is message2"
Output:
This is message1
This is message2
Related
New to Python, I'm trying to write a mass-email script (it will be sent via gmail).
Basically I have got it working, however I need to edit each message body separately with a greeting to name of the e-mail recipient. Currently the body is read off a body.txt file and I am not sure how to edit it. Example:
body.txt
Hi <recipient>,
Here is your voucher code.
And this is my script, send_email.py:
import smtplib
from email.mime.text import MIMEText
from_address = "xyz#gmail.com"
file = open("body.txt","rb")
message = MIMEText(file.read())
file.close()
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(from_address, "XXXXX")
server.set_debuglevel(1)
sender = from_address
recipients = ['abc#gmail.com', 'def#gmail.com']
message['Subject'] = "Voucher Code"
message['From'] = sender
message['To'] = ", ".join(recipients)
server.sendmail(sender, recipients, message.as_string())
I think most probably I will have to create another list with the names of recipients and match them to the email addresses to be sent to, but I'm not sure how to go from there. In fact, I'm not even sure this is the best way- it works though, so I'm pretty happy so far, apart from not being able to edit the body. How do I go from here?
I've seen what you did and I think it's a great start. To edit in the recipients name to the message is really quite simple. You need to format the string. Here's an example:
emailBody = "Hello {}, this is my email body. As you can see this program inserts every recipient separately. Hopefully it is helpful. \n"
recipients = ["Ann", "Josh", "Bob", "Marie"]
for recipient in recipients:
print(emailBody.format(recipient))
The main thing is that your emailBody must include symbols {} and then you can use
emailBody.format(content)
to put something into those brackets.
BACKGROUND
Regarding the following articles:
https://www.drupal.org/project/mimemail/issues/31524
Exclamation Point Randomly In Result of PHP HTML-Email
https://sourceforge.net/p/phpmailer/bugs/53/
All the problems and solutions refer to PHP issue, but I have run into this problem in Python.
If I send the emails directly to recipients, all is well, no exclamation marks appear, and the message displays properly.
However, utilizing our "Sympa" (https://www.sympa.org/) system that the University uses for it "mailing list" solution, emails from this system have the exclamation marks and line breaks inserted in the message and HTML breaks causing display issues.
The problem stems from line length. Any line longer than a magical 998 character length line gets this exclamation marks and line breaks inserted.
NOW THE QUESTION
One of the solutions they mention is encoding the body of a message in base64, which apparently is immune to the line length issue. However, I can not figure out how to properly form a message in Python and have the proper headers and encoding happen so the message will display properly in an email client.
Right now, I have only succeed in sending emails with base64 encode bodies as attached files. Bleck!
I need to send HTML encoded emails (tables and some formatting). I create one very long concatenated string of all the html squished together. It is ugly but will display properly.
HELP?!
NOTE: If anyone else has had this problem and has a solution that will allow me to send emails that are not plagued by line length issue, I am all ears!
Source Code as Requested
# Add support for emailing reports
import smtplib
# from email.mime.text import MIMEText
from email.mime.message import MIMEMessage
from email.encoders import encode_base64
from email.message import Message
... ...
headerData = {"rDate": datetime.datetime.now().strftime('%Y-%m-%d')}
msg_body = msg_header.format(**headerData) + contact_table + spacer + svc_table
theMsg = Message()
theMsg.set_payload(msg_body)
encode_base64(theMsg)
theMsg.add_header('Content-Transfer-Encoding', 'base64')
envelope = MIMEMessage(theMsg, 'html')
theSubject = "Audit for: "+aService['description']
envelope['Subject'] = theSubject
from_addr = "xxx#xxx"
envelope['From'] = from_addr
to_addrs = "xxx#xxxx"
# to_addrs = aService['contact_email']
envelope['To'] = to_addrs
# Send the message via our own SMTP server.
s = smtplib.SMTP('x.x.x.x')
s.sendmail(from_addr, to_addrs, envelope.as_string())
s.quit()
SOLUTION, thank you #Serge Ballesta
Going back to MIMEText, and specifying a character set seemed to do the trick:
envelope = MIMEText(msg_body, 'html', _charset='utf-8')
assert envelope['Content-Transfer-Encoding'] == 'base64'
envelope['Subject'] = "Audit for: "+aService['description']
from_addr = "f5-admins#utlists.utexas.edu"
envelope['From'] = from_addr
to_addrs = "xx-test#xx.xx.edu"
envelope['To'] = to_addrs
# Send the message via our own SMTP server.
s = smtplib.SMTP('xx.xx.xx.edu')
s.sendmail(from_addr, to_addrs, envelope.as_string())
s.quit()
Apparently I was just stabbing around and did not account for character set. Using MIMEText and not MIMEMessage.
Normally, email.mime.MIMEText automatically sets the Content-Transfert-Encoding to base64 if the body is not declared to be plain ASCII. So, assuming that body contains the HTML text of the body of the message (no mail headers there), declaring it as utf-8 should be enough:
msg = email.mime.text.MIMEText(body, 'html', _charset='utf-8')
# assert the cte:
assert msg['Content-Transfer-Encoding'] == 'base64'
theSubject = "Audit for: "+aService['description']
msg['Subject'] = theSubject
from_addr = "xxx#xxx"
msg['From'] = from_addr
to_addrs = "xxx#xxxx"
# to_addrs = aService['contact_email']
msg['To'] = to_addrs
# optionally set other headers
# msg['Date']=datetime.datetime.now().isoformat()
# Send the message
s = smtplib.SMTP('x.x.x.x')
s.sendmail(from_addr, to_addrs, msg.as_bytes())
s.quit()
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')
I tried hard to find solution to this issues but all in vein, finally i have to ask you guys. I have HTML email (using Python's smtplib). Here is the code
Message = """From: abc#abc.com>
To: abc#abc.com>
MIME-Version: 1.0
Content-type: text/html
Subject: test
Hello,
Following is the message
""" + '\n'.join(mail_body) + """
Thank you.
"""
In above code, mail_body is a list which contains lines of output from a process. Now what i want is, to display these lines (line by line) in HTML email. What is happening now its just appending line after line. i.e.
I am storing the output(of process) like this :
for line in cmd.stdout.readline()
mail_body.append()
Current Output in HTML email is:
Hello,
abc
Thank you.
What i want :
Hello,
a
b
c
Thank you.
I just want to attach my process output in HTML email line by line. Can my output be achieved in any way?
Thanks and Regards
You could generate the email content to send using email package (from stdlib) e.g.:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from cgi import escape
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from smtplib import SMTP_SSL
login, password = 'me#example.com', 'my password'
# create message
msg = MIMEMultipart('alternative')
msg['Subject'] = Header('subject…', 'utf-8')
msg['From'] = login
msg['To'] = ', '.join([login,])
# Create the body of the message (a plain-text and an HTML version).
text = "Hello,\nFollowing is the message\n%(somelist)s\n\nThank you." % dict(
somelist='\n'.join(["- " + item for item in mail_body]))
html = """<!DOCTYPE html><title></title><p>Hello,<p>Following is the message
<ul>%(somelist)s</ul><p>Thank you. """ % dict(
somelist='\n'.join(["<li> " + escape(item) for item in mail_body]))
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(MIMEText(text, 'plain', 'utf-8'))
msg.attach(MIMEText(html, 'html', 'utf-8'))
# send it
s = SMTP_SSL('smtp.mail.example.com', timeout=10) # no cert check on Python 2
s.set_debuglevel(0)
try:
s.login(login, password)
s.sendmail(msg['From'], msg['To'], msg.as_string())
finally:
s.quit()
in HTML, a new line is not \n it is <br> for "line break" but since you are also not using HTML tags in this email, you also need to know that in MIME messages, a newline is \r\n and not just \n
So you should write:
'\r\n'.join(mail_body)
For newlines that deal with the MIME message, but if you are going to use the HTML for formatting, then you need to know that <br> is the line break, and it would be:
'<br>'.join(mail_body)
To be comprehensive, you could try:
'\r\n<br>'.join(mail_body)
But I do now know what that would like like...
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')