Related
This code works and sends me an email just fine:
import smtplib
#SERVER = "localhost"
FROM = 'monty#python.com'
TO = ["jon#mycompany.com"] # must be a list
SUBJECT = "Hello!"
TEXT = "This message was sent with Python's smtplib."
# Prepare actual message
message = """\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
# Send the mail
server = smtplib.SMTP('myserver')
server.sendmail(FROM, TO, message)
server.quit()
However if I try to wrap it in a function like this:
def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
import smtplib
"""this is some test documentation in the function"""
message = """\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
# Send the mail
server = smtplib.SMTP(SERVER)
server.sendmail(FROM, TO, message)
server.quit()
and call it I get the following errors:
Traceback (most recent call last):
File "C:/Python31/mailtest1.py", line 8, in <module>
sendmail.sendMail(sender,recipients,subject,body,server)
File "C:/Python31\sendmail.py", line 13, in sendMail
server.sendmail(FROM, TO, message)
File "C:\Python31\lib\smtplib.py", line 720, in sendmail
self.rset()
File "C:\Python31\lib\smtplib.py", line 444, in rset
return self.docmd("rset")
File "C:\Python31\lib\smtplib.py", line 368, in docmd
return self.getreply()
File "C:\Python31\lib\smtplib.py", line 345, in getreply
raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed
Can anyone help me understand why?
I recommend that you use the standard packages email and smtplib together to send email. Please look at the following example (reproduced from the Python documentation). Notice that if you follow this approach, the "simple" task is indeed simple, and the more complex tasks (like attaching binary objects or sending plain/HTML multipart messages) are accomplished very rapidly.
# Import smtplib for the actual sending function
import smtplib
# Import the email modules we'll need
from email.mime.text import MIMEText
# Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters.
with open(textfile, 'rb') as fp:
# Create a text/plain message
msg = MIMEText(fp.read())
# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you
# Send the message via our own SMTP server, but don't include the
# envelope header.
s = smtplib.SMTP('localhost')
s.sendmail(me, [you], msg.as_string())
s.quit()
For sending email to multiple destinations, you can also follow the example in the Python documentation:
# Import smtplib for the actual sending function
import smtplib
# Here are the email package modules we'll need
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
# Create the container (outer) email message.
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'
# me == the sender's email address
# family = the list of all recipients' email addresses
msg['From'] = me
msg['To'] = ', '.join(family)
msg.preamble = 'Our family reunion'
# Assume we know that the image files are all in PNG format
for file in pngfiles:
# Open the files in binary mode. Let the MIMEImage class automatically
# guess the specific image type.
with open(file, 'rb') as fp:
img = MIMEImage(fp.read())
msg.attach(img)
# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.sendmail(me, family, msg.as_string())
s.quit()
As you can see, the header To in the MIMEText object must be a string consisting of email addresses separated by commas. On the other hand, the second argument to the sendmail function must be a list of strings (each string is an email address).
So, if you have three email addresses: person1#example.com, person2#example.com, and person3#example.com, you can do as follows (obvious sections omitted):
to = ["person1#example.com", "person2#example.com", "person3#example.com"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())
the ",".join(to) part makes a single string out of the list, separated by commas.
From your questions I gather that you have not gone through the Python tutorial - it is a MUST if you want to get anywhere in Python - the documentation is mostly excellent for the standard library.
When I need to mail in Python, I use the mailgun API which gets a lot of the headaches with sending mails sorted out. They have a wonderful app/api that allows you to send 5,000 free emails per month.
Sending an email would be like this:
def send_simple_message():
return requests.post(
"https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages",
auth=("api", "YOUR_API_KEY"),
data={"from": "Excited User <mailgun#YOUR_DOMAIN_NAME>",
"to": ["bar#example.com", "YOU#YOUR_DOMAIN_NAME"],
"subject": "Hello",
"text": "Testing some Mailgun awesomness!"})
You can also track events and lots more, see the quickstart guide.
I'd like to help you with sending emails by advising the yagmail package (I'm the maintainer, sorry for the advertising, but I feel it can really help!).
The whole code for you would be:
import yagmail
yag = yagmail.SMTP(FROM, 'pass')
yag.send(TO, SUBJECT, TEXT)
Note that I provide defaults for all arguments, for example if you want to send to yourself, you can omit TO, if you don't want a subject, you can omit it also.
Furthermore, the goal is also to make it really easy to attach html code or images (and other files).
Where you put contents you can do something like:
contents = ['Body text, and here is an embedded image:', 'http://somedomain/image.png',
'You can also find an audio file attached.', '/local/path/song.mp3']
Wow, how easy it is to send attachments! This would take like 20 lines without yagmail ;)
Also, if you set it up once, you'll never have to enter the password again (and have it safely stored). In your case you can do something like:
import yagmail
yagmail.SMTP().send(contents = contents)
which is much more concise!
I'd invite you to have a look at the github or install it directly with pip install yagmail.
Here is an example on Python 3.x, much simpler than 2.x:
import smtplib
from email.message import EmailMessage
def send_mail(to_email, subject, message, server='smtp.example.cn',
from_email='xx#example.com'):
# import smtplib
msg = EmailMessage()
msg['Subject'] = subject
msg['From'] = from_email
msg['To'] = ', '.join(to_email)
msg.set_content(message)
print(msg)
server = smtplib.SMTP(server)
server.set_debuglevel(1)
server.login(from_email, 'password') # user & password
server.send_message(msg)
server.quit()
print('successfully sent the mail.')
call this function:
send_mail(to_email=['12345#qq.com', '12345#126.com'],
subject='hello', message='Your analysis has done!')
below may only for Chinese user:
If you use 126/163, 网易邮箱, you need to set"客户端授权密码", like below:
ref: https://stackoverflow.com/a/41470149/2803344
https://docs.python.org/3/library/email.examples.html#email-examples
There is indentation problem. The code below will work:
import textwrap
def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
import smtplib
"""this is some test documentation in the function"""
message = textwrap.dedent("""\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT))
# Send the mail
server = smtplib.SMTP(SERVER)
server.sendmail(FROM, TO, message)
server.quit()
While indenting your code in the function (which is ok), you did also indent the lines of the raw message string. But leading white space implies folding (concatenation) of the header lines, as described in sections 2.2.3 and 3.2.3 of RFC 2822 - Internet Message Format:
Each header field is logically a single line of characters comprising
the field name, the colon, and the field body. For convenience
however, and to deal with the 998/78 character limitations per line,
the field body portion of a header field can be split into a multiple
line representation; this is called "folding".
In the function form of your sendmail call, all lines are starting with white space and so are "unfolded" (concatenated) and you are trying to send
From: monty#python.com To: jon#mycompany.com Subject: Hello! This message was sent with Python's smtplib.
Other than our mind suggests, smtplib will not understand the To: and Subject: headers any longer, because these names are only recognized at the beginning of a line. Instead smtplib will assume a very long sender email address:
monty#python.com To: jon#mycompany.com Subject: Hello! This message was sent with Python's smtplib.
This won't work and so comes your Exception.
The solution is simple: Just preserve the message string as it was before. This can be done by a function (as Zeeshan suggested) or right away in the source code:
import smtplib
def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
"""this is some test documentation in the function"""
message = """\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
# Send the mail
server = smtplib.SMTP(SERVER)
server.sendmail(FROM, TO, message)
server.quit()
Now the unfolding does not occur and you send
From: monty#python.com
To: jon#mycompany.com
Subject: Hello!
This message was sent with Python's smtplib.
which is what works and what was done by your old code.
Note that I was also preserving the empty line between headers and body to accommodate section 3.5 of the RFC (which is required) and put the include outside the function according to the Python style guide PEP-0008 (which is optional).
Make sure you have granted permission for both Sender and Receiver to send email and receive email from Unknown sources(External Sources) in Email Account.
import smtplib
#Ports 465 and 587 are intended for email client to email server communication - sending email
server = smtplib.SMTP('smtp.gmail.com', 587)
#starttls() is a way to take an existing insecure connection and upgrade it to a secure connection using SSL/TLS.
server.starttls()
#Next, log in to the server
server.login("#email", "#password")
msg = "Hello! This Message was sent by the help of Python"
#Send the mail
server.sendmail("#Sender", "#Reciever", msg)
It's probably putting tabs into your message. Print out message before you pass it to sendMail.
It's worth noting that the SMTP module supports the context manager so there is no need to manually call quit(), this will guarantee it is always called even if there is an exception.
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
server.ehlo()
server.login(user, password)
server.sendmail(from, to, body)
I haven't been satisfied with the package options for sending emails and I decided to make and open source my own email sender. It is easy to use and capable of advanced use cases.
To install:
pip install redmail
Usage:
from redmail import EmailSender
email = EmailSender(
host="<SMTP HOST ADDRESS>",
port=<PORT NUMBER>,
)
email.send(
sender="me#example.com",
receivers=["you#example.com"],
subject="An example email",
text="Hi, this is text body.",
html="<h1>Hi,</h1><p>this is HTML body</p>"
)
If your server requires a user and a password, just pass user_name and password to the EmailSender.
I have included a lot of features wrapped in the send method:
Include attachments
Include images directly to the HTML body
Jinja templating
Prettier HTML tables out of the box
Documentation:
https://red-mail.readthedocs.io/en/latest/
Source code: https://github.com/Miksus/red-mail
Thought I'd put in my two bits here since I have just figured out how this works.
It appears that you don't have the port specified on your SERVER connection settings, this effected me a little bit when I was trying to connect to my SMTP server that isn't using the default port: 25.
According to the smtplib.SMTP docs, your ehlo or helo request/response should automatically be taken care of, so you shouldn't have to worry about this (but might be something to confirm if all else fails).
Another thing to ask yourself is have you allowed SMTP connections on your SMTP server itself? For some sites like GMAIL and ZOHO you have to actually go in and activate the IMAP connections within the email account. Your mail server might not allow SMTP connections that don't come from 'localhost' perhaps? Something to look into.
The final thing is you might want to try and initiate the connection on TLS. Most servers now require this type of authentication.
You'll see I've jammed two TO fields into my email. The msg['TO'] and msg['FROM'] msg dictionary items allows the correct information to show up in the headers of the email itself, which one sees on the receiving end of the email in the To/From fields (you might even be able to add a Reply To field in here. The TO and FROM fields themselves are what the server requires. I know I've heard of some email servers rejecting emails if they don't have the proper email headers in place.
This is the code I've used, in a function, that works for me to email the content of a *.txt file using my local computer and a remote SMTP server (ZOHO as shown):
def emailResults(folder, filename):
# body of the message
doc = folder + filename + '.txt'
with open(doc, 'r') as readText:
msg = MIMEText(readText.read())
# headers
TO = 'to_user#domain.com'
msg['To'] = TO
FROM = 'from_user#domain.com'
msg['From'] = FROM
msg['Subject'] = 'email subject |' + filename
# SMTP
send = smtplib.SMTP('smtp.zoho.com', 587)
send.starttls()
send.login('from_user#domain.com', 'password')
send.sendmail(FROM, TO, msg.as_string())
send.quit()
Another implementation using gmail let's say:
import smtplib
def send_email(email_address: str, subject: str, body: str):
"""
send_email sends an email to the email address specified in the
argument.
Parameters
----------
email_address: email address of the recipient
subject: subject of the email
body: body of the email
"""
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login("email_address", "password")
server.sendmail("email_address", email_address,
"Subject: {}\n\n{}".format(subject, body))
server.quit()
I wrote a simple function send_email() for email sending with smtplib and email packages (link to my article). It additionally uses dotenv package to loads the sender email and password (please don't keep secrets in the code!). I was using Gmail for email service. The password was the App Password (here is Google docs on how to generate App Password).
import os
import smtplib
from email.message import EmailMessage
from dotenv import load_dotenv
_ = load_dotenv()
def send_email(to, subject, message):
try:
email_address = os.environ.get("EMAIL_ADDRESS")
email_password = os.environ.get("EMAIL_PASSWORD")
if email_address is None or email_password is None:
# no email address or password
# something is not configured properly
print("Did you set email address and password correctly?")
return False
# create email
msg = EmailMessage()
msg['Subject'] = subject
msg['From'] = email_address
msg['To'] = to
msg.set_content(message)
# send email
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login(email_address, email_password)
smtp.send_message(msg)
return True
except Exception as e:
print("Problem during send email")
print(str(e))
return False
The above approach is OK for simple email sending. If you are looking for more advanced features, such as HTML content or attachments - it, of course, can be hand-coded, but I would recommend using existing packages, for example yagmail.
Gmail has a limit of 500 emails per day. For sending many emails per day please consider transactional email service providers, like Amazon SES, MailGun, MailJet, or SendGrid.
import smtplib
s = smtplib.SMTP(your smtp server, smtp port) #SMTP session
message = "Hii!!!"
s.sendmail("sender", "Receiver", message) # sending the mail
s.quit() # terminating the session
just to complement the answer and so that your mail delivery system can be scalable.
I recommend having a configuration file (it can be .json, .yml, .ini, etc) with the sender's email configuration , password and recipients.
This way you can create different customizable items according to your needs.
Below is a small example with 3 files, config, functions and main. Text-only mailing.
config_email.ini
[email_1]
sender = test#test.com
password = XXXXXXXXXXX
recipients= ["email_2#test.com", "email_2#test.com"]
[email_2]
sender = test_2#test.com
password = XXXXXXXXXXX
recipients= ["email_2#test.com", "email_2#test.com", "email_3#test.com"]
These items will be called from main.py, which will return their respective values.
File with functions functions_email.py:
import smtplib,configparser,json
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def get_credentials(item):
parse = configparser.ConfigParser()
parse.read('config_email.ini')
sender = parse[item]['sender ']
password = parse[item]['password']
recipients= json.loads(parse[item]['recipients'])
return sender,password,recipients
def get_msg(sender,recipients,subject,mail_body):
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = ', '.join(recipients)
text = """\
"""+mail_body+""" """
part1 = MIMEText(text, "plain")
msg.attach(part1)
return msg
def send_email(msg,sender,password,recipients):
s = smtplib.SMTP('smtp.test.com')
s.login(sender,password)
s.sendmail(sender, recipients, msg.as_string())
s.quit()
File main.py:
from functions_email import *
sender,password,recipients = get_credenciales('email_2')
subject= 'text to subject'
mail_body = 'body....................'
msg = get_msg(sender,recipients ,subject,mail_body)
send_email(msg,sender,password,recipients)
Best regards!
import smtplib, ssl
port = 587 # For starttls
smtp_server = "smtp.office365.com"
sender_email = "170111018#student.mit.edu.tr"
receiver_email = "professordave#hotmail.com"
password = "12345678"
message = """\
Subject: Final exam
Teacher when is the final exam?"""
def SendMailf():
context = ssl.create_default_context()
with smtplib.SMTP(smtp_server, port) as server:
server.ehlo() # Can be omitted
server.starttls(context=context)
server.ehlo() # Can be omitted
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message)
print("mail send")
After a lot of fiddling with the examples e.g here
this now works for me:
import smtplib
from email.mime.text import MIMEText
# SMTP sendmail server mail relay
host = 'mail.server.com'
port = 587 # starttls not SSL 465 e.g gmail, port 25 blocked by most ISPs & AWS
sender_email = 'name#server.com'
recipient_email = 'name#domain.com'
password = 'YourSMTPServerAuthenticationPass'
subject = "Server - "
body = "Message from server"
def sendemail(host, port, sender_email, recipient_email, password, subject, body):
try:
p1 = f'<p><HR><BR>{recipient_email}<BR>'
p2 = f'<h2><font color="green">{subject}</font></h2>'
p3 = f'<p>{body}'
p4 = f'<p>Kind Regards,<BR><BR>{sender_email}<BR><HR>'
message = MIMEText((p1+p2+p3+p4), 'html')
# servers may not accept non RFC 5321 / RFC 5322 / compliant TXT & HTML typos
message['From'] = f'Sender Name <{sender_email}>'
message['To'] = f'Receiver Name <{recipient_email}>'
message['Cc'] = f'Receiver2 Name <>'
message['Subject'] = f'{subject}'
msg = message.as_string()
server = smtplib.SMTP(host, port)
print("Connection Status: Connected")
server.set_debuglevel(1)
server.ehlo()
server.starttls()
server.ehlo()
server.login(sender_email, password)
print("Connection Status: Logged in")
server.sendmail(sender_email, recipient_email, msg)
print("Status: Email as HTML successfully sent")
except Exception as e:
print(e)
print("Error: unable to send email")
# Run
sendemail(host, port, sender_email, recipient_email, password, subject, body)
print("Status: Exit")
As far your code is concerned, there doesn't seem to be anything fundamentally wrong with it except that, it is unclear how you're actually calling that function. All I can think of is that when your server is not responding then you will get this SMTPServerDisconnected error. If you lookup the getreply() function in smtplib (excerpt below), you will get an idea.
def getreply(self):
"""Get a reply from the server.
Returns a tuple consisting of:
- server response code (e.g. '250', or such, if all goes well)
Note: returns -1 if it can't read response code.
- server response string corresponding to response code (multiline
responses are converted to a single, multiline string).
Raises SMTPServerDisconnected if end-of-file is reached.
"""
check an example at https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py that also uses a function call to send an email, if that's what you're trying to do (DRY approach).
This code works and sends me an email just fine:
import smtplib
#SERVER = "localhost"
FROM = 'monty#python.com'
TO = ["jon#mycompany.com"] # must be a list
SUBJECT = "Hello!"
TEXT = "This message was sent with Python's smtplib."
# Prepare actual message
message = """\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
# Send the mail
server = smtplib.SMTP('myserver')
server.sendmail(FROM, TO, message)
server.quit()
However if I try to wrap it in a function like this:
def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
import smtplib
"""this is some test documentation in the function"""
message = """\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
# Send the mail
server = smtplib.SMTP(SERVER)
server.sendmail(FROM, TO, message)
server.quit()
and call it I get the following errors:
Traceback (most recent call last):
File "C:/Python31/mailtest1.py", line 8, in <module>
sendmail.sendMail(sender,recipients,subject,body,server)
File "C:/Python31\sendmail.py", line 13, in sendMail
server.sendmail(FROM, TO, message)
File "C:\Python31\lib\smtplib.py", line 720, in sendmail
self.rset()
File "C:\Python31\lib\smtplib.py", line 444, in rset
return self.docmd("rset")
File "C:\Python31\lib\smtplib.py", line 368, in docmd
return self.getreply()
File "C:\Python31\lib\smtplib.py", line 345, in getreply
raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed
Can anyone help me understand why?
I recommend that you use the standard packages email and smtplib together to send email. Please look at the following example (reproduced from the Python documentation). Notice that if you follow this approach, the "simple" task is indeed simple, and the more complex tasks (like attaching binary objects or sending plain/HTML multipart messages) are accomplished very rapidly.
# Import smtplib for the actual sending function
import smtplib
# Import the email modules we'll need
from email.mime.text import MIMEText
# Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters.
with open(textfile, 'rb') as fp:
# Create a text/plain message
msg = MIMEText(fp.read())
# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you
# Send the message via our own SMTP server, but don't include the
# envelope header.
s = smtplib.SMTP('localhost')
s.sendmail(me, [you], msg.as_string())
s.quit()
For sending email to multiple destinations, you can also follow the example in the Python documentation:
# Import smtplib for the actual sending function
import smtplib
# Here are the email package modules we'll need
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
# Create the container (outer) email message.
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'
# me == the sender's email address
# family = the list of all recipients' email addresses
msg['From'] = me
msg['To'] = ', '.join(family)
msg.preamble = 'Our family reunion'
# Assume we know that the image files are all in PNG format
for file in pngfiles:
# Open the files in binary mode. Let the MIMEImage class automatically
# guess the specific image type.
with open(file, 'rb') as fp:
img = MIMEImage(fp.read())
msg.attach(img)
# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.sendmail(me, family, msg.as_string())
s.quit()
As you can see, the header To in the MIMEText object must be a string consisting of email addresses separated by commas. On the other hand, the second argument to the sendmail function must be a list of strings (each string is an email address).
So, if you have three email addresses: person1#example.com, person2#example.com, and person3#example.com, you can do as follows (obvious sections omitted):
to = ["person1#example.com", "person2#example.com", "person3#example.com"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())
the ",".join(to) part makes a single string out of the list, separated by commas.
From your questions I gather that you have not gone through the Python tutorial - it is a MUST if you want to get anywhere in Python - the documentation is mostly excellent for the standard library.
When I need to mail in Python, I use the mailgun API which gets a lot of the headaches with sending mails sorted out. They have a wonderful app/api that allows you to send 5,000 free emails per month.
Sending an email would be like this:
def send_simple_message():
return requests.post(
"https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages",
auth=("api", "YOUR_API_KEY"),
data={"from": "Excited User <mailgun#YOUR_DOMAIN_NAME>",
"to": ["bar#example.com", "YOU#YOUR_DOMAIN_NAME"],
"subject": "Hello",
"text": "Testing some Mailgun awesomness!"})
You can also track events and lots more, see the quickstart guide.
I'd like to help you with sending emails by advising the yagmail package (I'm the maintainer, sorry for the advertising, but I feel it can really help!).
The whole code for you would be:
import yagmail
yag = yagmail.SMTP(FROM, 'pass')
yag.send(TO, SUBJECT, TEXT)
Note that I provide defaults for all arguments, for example if you want to send to yourself, you can omit TO, if you don't want a subject, you can omit it also.
Furthermore, the goal is also to make it really easy to attach html code or images (and other files).
Where you put contents you can do something like:
contents = ['Body text, and here is an embedded image:', 'http://somedomain/image.png',
'You can also find an audio file attached.', '/local/path/song.mp3']
Wow, how easy it is to send attachments! This would take like 20 lines without yagmail ;)
Also, if you set it up once, you'll never have to enter the password again (and have it safely stored). In your case you can do something like:
import yagmail
yagmail.SMTP().send(contents = contents)
which is much more concise!
I'd invite you to have a look at the github or install it directly with pip install yagmail.
Here is an example on Python 3.x, much simpler than 2.x:
import smtplib
from email.message import EmailMessage
def send_mail(to_email, subject, message, server='smtp.example.cn',
from_email='xx#example.com'):
# import smtplib
msg = EmailMessage()
msg['Subject'] = subject
msg['From'] = from_email
msg['To'] = ', '.join(to_email)
msg.set_content(message)
print(msg)
server = smtplib.SMTP(server)
server.set_debuglevel(1)
server.login(from_email, 'password') # user & password
server.send_message(msg)
server.quit()
print('successfully sent the mail.')
call this function:
send_mail(to_email=['12345#qq.com', '12345#126.com'],
subject='hello', message='Your analysis has done!')
below may only for Chinese user:
If you use 126/163, 网易邮箱, you need to set"客户端授权密码", like below:
ref: https://stackoverflow.com/a/41470149/2803344
https://docs.python.org/3/library/email.examples.html#email-examples
There is indentation problem. The code below will work:
import textwrap
def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
import smtplib
"""this is some test documentation in the function"""
message = textwrap.dedent("""\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT))
# Send the mail
server = smtplib.SMTP(SERVER)
server.sendmail(FROM, TO, message)
server.quit()
While indenting your code in the function (which is ok), you did also indent the lines of the raw message string. But leading white space implies folding (concatenation) of the header lines, as described in sections 2.2.3 and 3.2.3 of RFC 2822 - Internet Message Format:
Each header field is logically a single line of characters comprising
the field name, the colon, and the field body. For convenience
however, and to deal with the 998/78 character limitations per line,
the field body portion of a header field can be split into a multiple
line representation; this is called "folding".
In the function form of your sendmail call, all lines are starting with white space and so are "unfolded" (concatenated) and you are trying to send
From: monty#python.com To: jon#mycompany.com Subject: Hello! This message was sent with Python's smtplib.
Other than our mind suggests, smtplib will not understand the To: and Subject: headers any longer, because these names are only recognized at the beginning of a line. Instead smtplib will assume a very long sender email address:
monty#python.com To: jon#mycompany.com Subject: Hello! This message was sent with Python's smtplib.
This won't work and so comes your Exception.
The solution is simple: Just preserve the message string as it was before. This can be done by a function (as Zeeshan suggested) or right away in the source code:
import smtplib
def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
"""this is some test documentation in the function"""
message = """\
From: %s
To: %s
Subject: %s
%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
# Send the mail
server = smtplib.SMTP(SERVER)
server.sendmail(FROM, TO, message)
server.quit()
Now the unfolding does not occur and you send
From: monty#python.com
To: jon#mycompany.com
Subject: Hello!
This message was sent with Python's smtplib.
which is what works and what was done by your old code.
Note that I was also preserving the empty line between headers and body to accommodate section 3.5 of the RFC (which is required) and put the include outside the function according to the Python style guide PEP-0008 (which is optional).
Make sure you have granted permission for both Sender and Receiver to send email and receive email from Unknown sources(External Sources) in Email Account.
import smtplib
#Ports 465 and 587 are intended for email client to email server communication - sending email
server = smtplib.SMTP('smtp.gmail.com', 587)
#starttls() is a way to take an existing insecure connection and upgrade it to a secure connection using SSL/TLS.
server.starttls()
#Next, log in to the server
server.login("#email", "#password")
msg = "Hello! This Message was sent by the help of Python"
#Send the mail
server.sendmail("#Sender", "#Reciever", msg)
It's probably putting tabs into your message. Print out message before you pass it to sendMail.
It's worth noting that the SMTP module supports the context manager so there is no need to manually call quit(), this will guarantee it is always called even if there is an exception.
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
server.ehlo()
server.login(user, password)
server.sendmail(from, to, body)
I haven't been satisfied with the package options for sending emails and I decided to make and open source my own email sender. It is easy to use and capable of advanced use cases.
To install:
pip install redmail
Usage:
from redmail import EmailSender
email = EmailSender(
host="<SMTP HOST ADDRESS>",
port=<PORT NUMBER>,
)
email.send(
sender="me#example.com",
receivers=["you#example.com"],
subject="An example email",
text="Hi, this is text body.",
html="<h1>Hi,</h1><p>this is HTML body</p>"
)
If your server requires a user and a password, just pass user_name and password to the EmailSender.
I have included a lot of features wrapped in the send method:
Include attachments
Include images directly to the HTML body
Jinja templating
Prettier HTML tables out of the box
Documentation:
https://red-mail.readthedocs.io/en/latest/
Source code: https://github.com/Miksus/red-mail
Thought I'd put in my two bits here since I have just figured out how this works.
It appears that you don't have the port specified on your SERVER connection settings, this effected me a little bit when I was trying to connect to my SMTP server that isn't using the default port: 25.
According to the smtplib.SMTP docs, your ehlo or helo request/response should automatically be taken care of, so you shouldn't have to worry about this (but might be something to confirm if all else fails).
Another thing to ask yourself is have you allowed SMTP connections on your SMTP server itself? For some sites like GMAIL and ZOHO you have to actually go in and activate the IMAP connections within the email account. Your mail server might not allow SMTP connections that don't come from 'localhost' perhaps? Something to look into.
The final thing is you might want to try and initiate the connection on TLS. Most servers now require this type of authentication.
You'll see I've jammed two TO fields into my email. The msg['TO'] and msg['FROM'] msg dictionary items allows the correct information to show up in the headers of the email itself, which one sees on the receiving end of the email in the To/From fields (you might even be able to add a Reply To field in here. The TO and FROM fields themselves are what the server requires. I know I've heard of some email servers rejecting emails if they don't have the proper email headers in place.
This is the code I've used, in a function, that works for me to email the content of a *.txt file using my local computer and a remote SMTP server (ZOHO as shown):
def emailResults(folder, filename):
# body of the message
doc = folder + filename + '.txt'
with open(doc, 'r') as readText:
msg = MIMEText(readText.read())
# headers
TO = 'to_user#domain.com'
msg['To'] = TO
FROM = 'from_user#domain.com'
msg['From'] = FROM
msg['Subject'] = 'email subject |' + filename
# SMTP
send = smtplib.SMTP('smtp.zoho.com', 587)
send.starttls()
send.login('from_user#domain.com', 'password')
send.sendmail(FROM, TO, msg.as_string())
send.quit()
Another implementation using gmail let's say:
import smtplib
def send_email(email_address: str, subject: str, body: str):
"""
send_email sends an email to the email address specified in the
argument.
Parameters
----------
email_address: email address of the recipient
subject: subject of the email
body: body of the email
"""
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login("email_address", "password")
server.sendmail("email_address", email_address,
"Subject: {}\n\n{}".format(subject, body))
server.quit()
I wrote a simple function send_email() for email sending with smtplib and email packages (link to my article). It additionally uses dotenv package to loads the sender email and password (please don't keep secrets in the code!). I was using Gmail for email service. The password was the App Password (here is Google docs on how to generate App Password).
import os
import smtplib
from email.message import EmailMessage
from dotenv import load_dotenv
_ = load_dotenv()
def send_email(to, subject, message):
try:
email_address = os.environ.get("EMAIL_ADDRESS")
email_password = os.environ.get("EMAIL_PASSWORD")
if email_address is None or email_password is None:
# no email address or password
# something is not configured properly
print("Did you set email address and password correctly?")
return False
# create email
msg = EmailMessage()
msg['Subject'] = subject
msg['From'] = email_address
msg['To'] = to
msg.set_content(message)
# send email
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login(email_address, email_password)
smtp.send_message(msg)
return True
except Exception as e:
print("Problem during send email")
print(str(e))
return False
The above approach is OK for simple email sending. If you are looking for more advanced features, such as HTML content or attachments - it, of course, can be hand-coded, but I would recommend using existing packages, for example yagmail.
Gmail has a limit of 500 emails per day. For sending many emails per day please consider transactional email service providers, like Amazon SES, MailGun, MailJet, or SendGrid.
import smtplib
s = smtplib.SMTP(your smtp server, smtp port) #SMTP session
message = "Hii!!!"
s.sendmail("sender", "Receiver", message) # sending the mail
s.quit() # terminating the session
just to complement the answer and so that your mail delivery system can be scalable.
I recommend having a configuration file (it can be .json, .yml, .ini, etc) with the sender's email configuration , password and recipients.
This way you can create different customizable items according to your needs.
Below is a small example with 3 files, config, functions and main. Text-only mailing.
config_email.ini
[email_1]
sender = test#test.com
password = XXXXXXXXXXX
recipients= ["email_2#test.com", "email_2#test.com"]
[email_2]
sender = test_2#test.com
password = XXXXXXXXXXX
recipients= ["email_2#test.com", "email_2#test.com", "email_3#test.com"]
These items will be called from main.py, which will return their respective values.
File with functions functions_email.py:
import smtplib,configparser,json
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def get_credentials(item):
parse = configparser.ConfigParser()
parse.read('config_email.ini')
sender = parse[item]['sender ']
password = parse[item]['password']
recipients= json.loads(parse[item]['recipients'])
return sender,password,recipients
def get_msg(sender,recipients,subject,mail_body):
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = ', '.join(recipients)
text = """\
"""+mail_body+""" """
part1 = MIMEText(text, "plain")
msg.attach(part1)
return msg
def send_email(msg,sender,password,recipients):
s = smtplib.SMTP('smtp.test.com')
s.login(sender,password)
s.sendmail(sender, recipients, msg.as_string())
s.quit()
File main.py:
from functions_email import *
sender,password,recipients = get_credenciales('email_2')
subject= 'text to subject'
mail_body = 'body....................'
msg = get_msg(sender,recipients ,subject,mail_body)
send_email(msg,sender,password,recipients)
Best regards!
import smtplib, ssl
port = 587 # For starttls
smtp_server = "smtp.office365.com"
sender_email = "170111018#student.mit.edu.tr"
receiver_email = "professordave#hotmail.com"
password = "12345678"
message = """\
Subject: Final exam
Teacher when is the final exam?"""
def SendMailf():
context = ssl.create_default_context()
with smtplib.SMTP(smtp_server, port) as server:
server.ehlo() # Can be omitted
server.starttls(context=context)
server.ehlo() # Can be omitted
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message)
print("mail send")
After a lot of fiddling with the examples e.g here
this now works for me:
import smtplib
from email.mime.text import MIMEText
# SMTP sendmail server mail relay
host = 'mail.server.com'
port = 587 # starttls not SSL 465 e.g gmail, port 25 blocked by most ISPs & AWS
sender_email = 'name#server.com'
recipient_email = 'name#domain.com'
password = 'YourSMTPServerAuthenticationPass'
subject = "Server - "
body = "Message from server"
def sendemail(host, port, sender_email, recipient_email, password, subject, body):
try:
p1 = f'<p><HR><BR>{recipient_email}<BR>'
p2 = f'<h2><font color="green">{subject}</font></h2>'
p3 = f'<p>{body}'
p4 = f'<p>Kind Regards,<BR><BR>{sender_email}<BR><HR>'
message = MIMEText((p1+p2+p3+p4), 'html')
# servers may not accept non RFC 5321 / RFC 5322 / compliant TXT & HTML typos
message['From'] = f'Sender Name <{sender_email}>'
message['To'] = f'Receiver Name <{recipient_email}>'
message['Cc'] = f'Receiver2 Name <>'
message['Subject'] = f'{subject}'
msg = message.as_string()
server = smtplib.SMTP(host, port)
print("Connection Status: Connected")
server.set_debuglevel(1)
server.ehlo()
server.starttls()
server.ehlo()
server.login(sender_email, password)
print("Connection Status: Logged in")
server.sendmail(sender_email, recipient_email, msg)
print("Status: Email as HTML successfully sent")
except Exception as e:
print(e)
print("Error: unable to send email")
# Run
sendemail(host, port, sender_email, recipient_email, password, subject, body)
print("Status: Exit")
As far your code is concerned, there doesn't seem to be anything fundamentally wrong with it except that, it is unclear how you're actually calling that function. All I can think of is that when your server is not responding then you will get this SMTPServerDisconnected error. If you lookup the getreply() function in smtplib (excerpt below), you will get an idea.
def getreply(self):
"""Get a reply from the server.
Returns a tuple consisting of:
- server response code (e.g. '250', or such, if all goes well)
Note: returns -1 if it can't read response code.
- server response string corresponding to response code (multiline
responses are converted to a single, multiline string).
Raises SMTPServerDisconnected if end-of-file is reached.
"""
check an example at https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py that also uses a function call to send an email, if that's what you're trying to do (DRY approach).
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.
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 am trying to send an email using Python script but somehow in the format I am expecting the email to come in my mailbox, it is not coming in that format. Below is my method which is sending an email -
def send_mail(data):
sender = 'fromuser#host.com'
receivers = ['touser#host.com']
message = """From: fromuser#host.com
To: touser#host.com
Subject: Send mail from python!!
"""
body = 'Some Text\n'
for item in data:
body = body + '{name} - {res}\n'.format(name=item['name'], res=item['res'])
message = message + body
try:
smtpObj = smtplib.SMTP('corp.host.com' )
smtpObj.sendmail(sender, receivers, message)
print "Mail sent"
except smtplib.SMTPException:
print "You can't spam. Mail sending failed!"
Here data just have key - value pair.
And I am getting an email like this in my Outlook mailbox -
In the From: section in my outlook, below string is coming as it is which is wrong -
fromuser#host.com To: touser#host.com Subject: Send mail from python!!
And To: , Subject: section is coming as Empty which is also wrong.
And in the body I am seeing everything coming in a single line but I want the result to be shown as -
Some Text
machinA - 0
machineB - 0
machineC - 0
How can I represent my data to be shown like this in my Outlook mailbox?
Since triple quoting preserves all spaces, you were accidentally sending:
From: fromuser#host.com
To: touser#host.com
Subject: Send mail from python!!
This invokes header unfolding: indented lines mean that the header is continued. So this really is a badly formatted From header. You need to make sure there are no extraneous spaces. This fixes up your current example:
def send_mail(data):
sender = 'fromuser#host.com'
receivers = ['touser#host.com']
message = """\
From: fromuser#host.com
To: touser#host.com
Subject: Send mail from python!!
"""
body = '\n\nSome Text\n'
for item in data:
body = body + '{name} - {res}\n'.format(name=item['name'], res=item['res'])
message = message + body
try:
smtpObj = smtplib.SMTP('corp.host.com' )
smtpObj.sendmail(sender, receivers, message)
print "Mail sent"
except smtplib.SMTPException:
print "You can't spam. Mail sending failed!"
However, you shouldn't be manually constructing messages at all. Python includes an assortment of lovely classes in email.message for constructing a message.
import email.message
m = email.message.Message()
m['From'] = "fromuser#host.com"
m['To'] = "touser#host.com"
m['Subject'] = "Send mail from python!!"
m.set_payload("Your text only body");
Now, you can turn your message into a string:
>>> m.as_string()
'To: touser#host.com\nFrom: fromuser#host.com\nSubject: Send mail from python!!\n\nyour text-only body'
I will warn you, properly dealing with email is a very large and complex topic, and if you want to use non-ascii, attachments, etc, there's a bit of a learning curve, and you will need to use all the faculties of the email.message library, which has a lot of documentation you should read and understand.