120

I'm using the following method to send mail from Python using SMTP. Is it the right method to use or are there gotchas I'm missing ?

from smtplib import SMTP
import datetime

debuglevel = 0

smtp = SMTP()
smtp.set_debuglevel(debuglevel)
smtp.connect('YOUR.MAIL.SERVER', 26)
smtp.login('USERNAME@DOMAIN', 'PASSWORD')

from_addr = "John Doe <john@doe.net>"
to_addr = "foo@bar.com"

subj = "hello"
date = datetime.datetime.now().strftime( "%d/%m/%Y %H:%M" )

message_text = "Hello\nThis is a mail from your server\n\nBye\n"

msg = "From: %s\nTo: %s\nSubject: %s\nDate: %s\n\n%s" 
        % ( from_addr, to_addr, subj, date, message_text )

smtp.sendmail(from_addr, to_addr, msg)
smtp.quit()
Eli Bendersky
  • 231,995
  • 78
  • 333
  • 394
  • 2
    Make sure you get the Date/Time correct. I found the following function quite useful, that gives you a perfectly formatted value for the Date-Header: http://docs.python.org/py3k/library/email.util.html#email.utils.formatdate – BastiBen Jul 30 '10 at 06:11
  • here's a code example that allows to [send email with Unicode text in subject and/or in body](http://stackoverflow.com/a/20787826/4279) – jfs Dec 26 '13 at 18:10
  • here's a code example that demonstrate how to [send images inline (plus email with both html and plain text parts)](http://stackoverflow.com/a/20485764/4279). It also shows how to configure ssl parameters on old Python versions. – jfs Dec 26 '13 at 18:11
  • 2
    Note that there are wrapper libraries available that make it a lot less code to send emails (such as [yagmail](https://pypi.python.org/pypi/yagmail)) – PascalVKooten Apr 17 '15 at 23:57

14 Answers14

113

The script I use is quite similar; I post it here as an example of how to use the email.* modules to generate MIME messages; so this script can be easily modified to attach pictures, etc.

I rely on my ISP to add the date time header.

My ISP requires me to use a secure smtp connection to send mail, I rely on the smtplib module (downloadable at http://www1.cs.columbia.edu/~db2501/ssmtplib.py)

As in your script, the username and password, (given dummy values below), used to authenticate on the SMTP server, are in plain text in the source. This is a security weakness; but the best alternative depends on how careful you need (want?) to be about protecting these.

=======================================

#! /usr/local/bin/python


SMTPserver = 'smtp.att.yahoo.com'
sender =     'me@my_email_domain.net'
destination = ['recipient@her_email_domain.com']

USERNAME = "USER_NAME_FOR_INTERNET_SERVICE_PROVIDER"
PASSWORD = "PASSWORD_INTERNET_SERVICE_PROVIDER"

# typical values for text_subtype are plain, html, xml
text_subtype = 'plain'


content="""\
Test message
"""

subject="Sent from Python"

import sys
import os
import re

from smtplib import SMTP_SSL as SMTP       # this invokes the secure SMTP protocol (port 465, uses SSL)
# from smtplib import SMTP                  # use this for standard SMTP protocol   (port 25, no encryption)

# old version
# from email.MIMEText import MIMEText
from email.mime.text import MIMEText

try:
    msg = MIMEText(content, text_subtype)
    msg['Subject']=       subject
    msg['From']   = sender # some SMTP servers will do this automatically, not all

    conn = SMTP(SMTPserver)
    conn.set_debuglevel(False)
    conn.login(USERNAME, PASSWORD)
    try:
        conn.sendmail(sender, destination, msg.as_string())
    finally:
        conn.quit()

except:
    sys.exit( "mail failed; %s" % "CUSTOM_ERROR" ) # give an error message
Benj
  • 518
  • 6
  • 17
Vincent Marchetti
  • 4,380
  • 3
  • 16
  • 8
  • 1
    @Vincent: mail failed; 'module' object has no attribute 'SSLFakeSocket' - using Gmail :( – RadiantHex Apr 26 '10 at 09:08
  • This sounds like a version or import problem, to help track it down: What version Python are you running? -- Do you need to connect to your SMTP server over SSL (and if so are you importing ssmtplib, as above)? Can you import smtplib directly from python interactive, if so, is there a smtplib.SSLFakeSocket class defined? Hope I can help – Vincent Marchetti Apr 28 '10 at 11:22
  • 2
    Use smtplib.SMTP_SSL (standard in latest versions of Python) to create the connection instead of ssmtplib.STMP_SSL (third party module hinted above). Notice the standard module begins with a single 's'. That worked for me. – Julio Gorgé Sep 21 '10 at 18:14
  • 2
    replace `from ssmtplib import SMTP_SSL as SMTP` with `from smtplib import SMTP_SSL as SMTP`, and this example would work from the standard Python library. – Adam Matan Jan 23 '11 at 14:20
  • 9
    Add `msg['To'] = ','.join(destination)`, Otherwise destination is not viewed in gmail – Taha Jahangir Apr 19 '12 at 03:08
  • This script worked for me. In USERNAME and PASSWORD section you should put username and password for google, not internet provider. – Denis Kutlubaev Feb 21 '15 at 13:11
  • This answer would be improved by consistent quote style as per [PEP8](https://www.python.org/dev/peps/pep-0008/#string-quotes) – Langston May 20 '15 at 03:32
  • import for MIMEText in python 2.7 and 3.5+ is `from email.mime.text import MIMEText` – shapiromatron Jan 04 '16 at 15:12
  • **Attention:** The previous version of the answer included the line: `conn.close()` It must be: `conn.quit()`, because `close()` will not terminate the TLS-connection properly! `close()` will be called during `quit()`. – aronadaal Mar 23 '16 at 07:57
90

The method I commonly use...not much different but a little bit

import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText

msg = MIMEMultipart()
msg['From'] = 'me@gmail.com'
msg['To'] = 'you@gmail.com'
msg['Subject'] = 'simple email in python'
message = 'here is the email'
msg.attach(MIMEText(message))

mailserver = smtplib.SMTP('smtp.gmail.com',587)
# identify ourselves to smtp gmail client
mailserver.ehlo()
# secure our email with tls encryption
mailserver.starttls()
# re-identify ourselves as an encrypted connection
mailserver.ehlo()
mailserver.login('me@gmail.com', 'mypassword')

mailserver.sendmail('me@gmail.com','you@gmail.com',msg.as_string())

mailserver.quit()

That's it

madman2890
  • 1,632
  • 1
  • 15
  • 15
  • If you use 2 Step Verification, you have to create an App specific password first and replace your normal password with it. See [Sign in using App Passwords](https://support.google.com/accounts/answer/185833?hl=en) – Suzana Jul 06 '14 at 14:45
  • 2
    I agree, this is the best answer and should be accepted. The one which is actually accepted is inferior. – SmallChess Apr 12 '16 at 05:51
  • 8
    For python3, use: `from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText` – art Apr 16 '18 at 15:34
23

Also if you want to do smtp auth with TLS as opposed to SSL then you just have to change the port (use 587) and do smtp.starttls(). This worked for me:

...
smtp.connect('YOUR.MAIL.SERVER', 587)
smtp.ehlo()
smtp.starttls()
smtp.ehlo()
smtp.login('USERNAME@DOMAIN', 'PASSWORD')
...
6

What about this?

import smtplib

SERVER = "localhost"

FROM = "sender@example.com"
TO = ["user@example.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(SERVER)
server.sendmail(FROM, TO, message)
server.quit()
Satish
  • 13,709
  • 26
  • 80
  • 130
6

The main gotcha I see is that you're not handling any errors: .login() and .sendmail() both have documented exceptions that they can throw, and it seems like .connect() must have some way to indicate that it was unable to connect - probably an exception thrown by the underlying socket code.

pjz
  • 38,171
  • 5
  • 45
  • 60
6

Make sure you don't have any firewalls blocking SMTP. The first time I tried to send an email, it was blocked both by Windows Firewall and McAfee - took forever to find them both.

Mark Ransom
  • 271,357
  • 39
  • 345
  • 578
4

following code is working fine for me:

import smtplib

to = 'mkyong2002@yahoo.com'
gmail_user = 'mkyong2002@gmail.com'
gmail_pwd = 'yourpassword'
smtpserver = smtplib.SMTP("smtp.gmail.com",587)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo() # extra characters to permit edit
smtpserver.login(gmail_user, gmail_pwd)
header = 'To:' + to + '\n' + 'From: ' + gmail_user + '\n' + 'Subject:testing \n'
print header
msg = header + '\n this is test msg from mkyong.com \n\n'
smtpserver.sendmail(gmail_user, to, msg)
print 'done!'
smtpserver.quit()

Ref: http://www.mkyong.com/python/how-do-send-email-in-python-via-smtplib/

aronadaal
  • 8,356
  • 1
  • 17
  • 33
Abdul Majeed
  • 2,275
  • 18
  • 25
  • 1
    Flask has a framework for email: from flask.ext.mail import Mail. I am troubleshooting it, and thought I would go back to Python code to see if I could get something to work. I liked this answer because it was bare bones. Oh yeah, and it worked! –  Aug 24 '15 at 18:09
  • **Attention:** The previous version of the answer included the line: `smtpserver.close()` It must be: `smtpserver.quit()`, because `close()` will not terminate the TLS-connection properly! `close()` will be called during `quit()`. – aronadaal Mar 23 '16 at 07:58
  • Hi, I am having trouble runnign teh above commands. when I use smtpserver.starttls(), I get an SMTP error "SMTPServerDisconnected: Connection unexpectedly closed: [Errno 10054]".. reported in https://stackoverflow.com/questions/46094175/starttls-is-giving-me-a-smtpserverdisconnect-error-when-trying-to-send-emails – fazkan Sep 07 '17 at 12:40
4

The example code which i did for send mail using SMTP.

import smtplib, ssl

smtp_server = "smtp.gmail.com"
port = 587  # For starttls
sender_email = "sender@email"
receiver_email = "receiver@email"
password = "<your password here>"
message = """ Subject: Hi there

This message is sent from Python."""


# Create a secure SSL context
context = ssl.create_default_context()

# Try to log in to server and send email
server = smtplib.SMTP(smtp_server,port)

try:
    server.ehlo() # Can be omitted
    server.starttls(context=context) # Secure the connection
    server.ehlo() # Can be omitted
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, message)
except Exception as e:
    # Print any error messages to stdout
    print(e)
finally:
    server.quit()
Hariharan AR
  • 928
  • 8
  • 17
3

You should make sure you format the date in the correct format - RFC2822.

Douglas Leeder
  • 49,001
  • 8
  • 86
  • 133
2

See all those lenghty answers? Please allow me to self promote by doing it all in a couple of lines.

Import and Connect:

import yagmail
yag = yagmail.SMTP('john@doe.net', host = 'YOUR.MAIL.SERVER', port = 26)

Then it is just a one-liner:

yag.send('foo@bar.com', 'hello', 'Hello\nThis is a mail from your server\n\nBye\n')

It will actually close when it goes out of scope (or can be closed manually). Furthermore, it will allow you to register your username in your keyring such that you do not have to write out your password in your script (it really bothered me prior to writing yagmail!)

For the package/installation, tips and tricks please look at git or pip, available for both Python 2 and 3.

PascalVKooten
  • 18,070
  • 15
  • 82
  • 140
  • @PascalvKoolen I installed yagmail, and tried connecting by giving my email id and password. but it gave me an authentication error – fazkan Sep 07 '17 at 13:47
2

Based on this example I made following function:

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def send_email(host, port, user, pwd, recipients, subject, body, html=None, from_=None):
    """ copied and adapted from
        https://stackoverflow.com/questions/10147455/how-to-send-an-email-with-gmail-as-provider-using-python#12424439
    returns None if all ok, but if problem then returns exception object
    """

    PORT_LIST = (25, 587, 465)

    FROM = from_ if from_ else user 
    TO = recipients if isinstance(recipients, (list, tuple)) else [recipients]
    SUBJECT = subject
    TEXT = body.encode("utf8") if isinstance(body, unicode) else body
    HTML = html.encode("utf8") if isinstance(html, unicode) else html

    if not html:
        # Prepare actual message
        message = """From: %s\nTo: %s\nSubject: %s\n\n%s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    else:
                # https://stackoverflow.com/questions/882712/sending-html-email-using-python#882770
        msg = MIMEMultipart('alternative')
        msg['Subject'] = SUBJECT
        msg['From'] = FROM
        msg['To'] = ", ".join(TO)

        # Record the MIME types of both parts - text/plain and text/html.
        # utf-8 -> https://stackoverflow.com/questions/5910104/python-how-to-send-utf-8-e-mail#5910530
        part1 = MIMEText(TEXT, 'plain', "utf-8")
        part2 = MIMEText(HTML, 'html', "utf-8")

        # Attach parts into message container.
        # According to RFC 2046, the last part of a multipart message, in this case
        # the HTML message, is best and preferred.
        msg.attach(part1)
        msg.attach(part2)

        message = msg.as_string()


    try:
        if port not in PORT_LIST: 
            raise Exception("Port %s not one of %s" % (port, PORT_LIST))

        if port in (465,):
            server = smtplib.SMTP_SSL(host, port)
        else:
            server = smtplib.SMTP(host, port)

        # optional
        server.ehlo()

        if port in (587,): 
            server.starttls()

        server.login(user, pwd)
        server.sendmail(FROM, TO, message)
        server.close()
        # logger.info("SENT_EMAIL to %s: %s" % (recipients, subject))
    except Exception, ex:
        return ex

    return None

if you pass only body then plain text mail will be sent, but if you pass html argument along with body argument, html email will be sent (with fallback to text content for email clients that don't support html/mime types).

Example usage:

ex = send_email(
      host        = 'smtp.gmail.com'
   #, port        = 465 # OK
    , port        = 587  #OK
    , user        = "xxx@gmail.com"
    , pwd         = "xxx"
    , from_       = 'xxx@gmail.com'
    , recipients  = ['yyy@gmail.com']
    , subject     = "Test from python"
    , body        = "Test from python - body"
    )
if ex: 
    print("Mail sending failed: %s" % ex)
else:
    print("OK - mail sent"

Btw. If you want to use gmail as testing or production SMTP server, enable temp or permanent access to less secured apps:

Robert Lujo
  • 12,924
  • 4
  • 46
  • 64
1

Or

import smtplib
 
from email.message import EmailMessage
from getpass import getpass


password = getpass()

message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = "USERNAME@DOMAIN"
message['To'] = "you@mail.com"

try:
    smtp_server = None
    smtp_server = smtplib.SMTP("YOUR.MAIL.SERVER", 587)
    smtp_server.ehlo()
    smtp_server.starttls()
    smtp_server.ehlo()
    smtp_server.login("USERNAME@DOMAIN", password)
    smtp_server.send_message(message)
except Exception as e:
    print("Error: ", str(e))
finally:
    if smtp_server is not None:
        smtp_server.quit()

If you want to use Port 465 you have to create an SMTP_SSL object.

Milovan Tomašević
  • 1,206
  • 1
  • 7
  • 13
0

you can do like that

import smtplib
from email.mime.text import MIMEText
from email.header import Header


server = smtplib.SMTP('mail.servername.com', 25)
server.ehlo()
server.starttls()

server.login('username', 'password')
from = 'me@servername.com'
to = 'mygfriend@servername.com'
body = 'That A Message For My Girl Friend For tell Him If We will go to eat Something This Nigth'
subject = 'Invite to A Diner'
msg = MIMEText(body,'plain','utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = Header(from, 'utf-8')
msg['To'] = Header(to, 'utf-8')
message = msg.as_string()
server.sendmail(from, to, message)
Skiller Dz
  • 739
  • 7
  • 16
0

Here's a working example for Python 3.x

#!/usr/bin/env python3

from email.message import EmailMessage
from getpass import getpass
from smtplib import SMTP_SSL
from sys import exit

smtp_server = 'smtp.gmail.com'
username = 'your_email_address@gmail.com'
password = getpass('Enter Gmail password: ')

sender = 'your_email_address@gmail.com'
destination = 'recipient_email_address@gmail.com'
subject = 'Sent from Python 3.x'
content = 'Hello! This was sent to you via Python 3.x!'

# Create a text/plain message
msg = EmailMessage()
msg.set_content(content)

msg['Subject'] = subject
msg['From'] = sender
msg['To'] = destination

try:
    s = SMTP_SSL(smtp_server)
    s.login(username, password)
    try:
        s.send_message(msg)
    finally:
        s.quit()

except Exception as E:
    exit('Mail failed: {}'.format(str(E)))
Mark
  • 629
  • 10
  • 19