Python

Python Send Email Example

In this example, by using Python 3.4, we’ll learn how to send mails using Python’s smtplib module.

SMTP stands for Simple Mail Transfer Protocol. It’s a protocol which handles sending e-mails and routing them between mail servers.

To send a mail, we need the host and port of a server, then we send to this server the message with a sender and a list of receivers.

The message is not just any string. This protocol expects the message in a certain format, which defines three headers (From, To and Subject) and the actual message, these separated with a blank line as in:

message = """From: Example Sender <sender@example.com>
To: Example Receiver <receiver@example.com>
Subject: Example Message

This is an example message. Sorry if you receive this by mistake."""

1. The Basics of smtplib

The smtplib module provides an SMTP client session object that can be used to send mails to any Internet machine with an SMTP or ESMTP listener daemon. Its constructor receives five optional parameters:

  • host: This is the host running the SMTP server. You can specifiy IP address of the host or a domain name like webcodegeeks.com.
  • port: When you provide a host argument, you need to specify the port your server is listening.
  • local_hostname: If your SMTP server is running locally, you can specify “localhost” here.
  • timeout: Specifies a timeout in seconds for blocking operations like the connection attempt.
  • source_address: Allows to bind to some specific source address in a machine with multiple network interfaces, and/or to some specific source TCP port.

An instance of this object encapsulates an SMTP connection. If the optional host and port parameters are given, the SMTP connect() method is called with those parameters during initialization. If specified, local_hostname is used as the FQDN of the local host in the HELO/EHLO command. Otherwise, the local hostname is found using socket.getfqdn(). If the connect() call returns anything other than a success code, an SMTPConnectError is raised. If the timeout expires, socket.timeout is raised. The optional source_address parameter takes a 2-tuple (host, port), for the socket to bind to as its source address before connecting. If omitted (or if host or port are '' and/or 0 respectively) the OS default behavior will be used.

Once this session object is created, it will provide a function called sendmail which receives three arguments (sender, receivers and message). sender is the e-mail address from which this mail will be sent, receivers is an array of e-mail addresses which will be the recipients, and message is the body of the mail, it should be formatted as we talked before.

Let’s see an example of how to apply the knowledge we acquired so far.

simple.py

import smtplib

sender = "sender@example.com"
receivers = ["receiver1@example.com", "receiver2@example.com"]


def format_mail(mail):
    return "{} ".format(mail.split("@")[0], mail)

message = """From: {}
To: {}
Subject: Example Subject

This is a test mail example
""".format("{} ".format(sender.split("@")[0], sender), ", ".join(map(format_mail, receivers)))

try:
    print("sending message: " + message)
    with smtplib.SMTP('example-smpt.com', 25) as session:
        session.sendmail(sender, receivers, message)
    print("message sent")
except smtplib.SMTPException:
    print("could not send mail")

Of course, the SMPT server defined in this example does not exist, so this example won’t actually work. In fact, as we need passwords to send mails (we’ll see this below), none of the examples in this article will actually work, unless you replace the credentials in them with ones of your own.

Having said that, let’s talk about this simple example. It’s just a really simple example which defines a sender, an array of recipients and a message (formatted as it should be, and dynamically inserting the addresses). Then it creates a session and calls sendmail. Notice the use of the with statement, when used like this, the SMTP QUIT command is issued automatically when the with statement exits.

2. SSL and Authentication

Let’s talk bout login. Usually we send a mail from an account to which we have access, and it’s secured (most of the times), so authentication will be required. Also, connections to an SMTP server should be done via SSL from the beginning of the connection. In the next example we’ll see both login and SSL connections procedures.

Python provides a class called SMTP_SSL, which behaves exactly like SMTP. It’s constructor receives almost the same arguments. If the host is not specified, it will use the local host instead. If port is zero, it will use the default for SSL SMTP connections, which is 465. The optional arguments local_hostname, timeout and source_address have the same meaning as they do in the SMTP class. There are also three more arguments. The first one is context, which is optional, and can contain a SSLContext and allows to configure various aspects of the secure connection. The other ones are keyfile and certfile, which are a legacy alternative to context, and can point to a PEM formatted private key and certificate chain file for the SSL connection.

Now, both SMTP and SMTP_SSL provide a function called login which receives a user and a password, and is our way through authentication. The next example will show how to use SMTP_SSL and the login function, but keep in mind that this function (and almost every other) behave the same in both SMTP and SMTP_SSL.

login_ssl.py

import smtplib

sender = "sender@example.com"
receivers = ["receiver@example.com"]


def format_mail(mail):
    return "{} ".format(mail.split("@")[0], mail)

message = """From: {}
To: {}
Subject: Example Subject

This is a test mail example
""".format("{} ".format(sender.split("@")[0], sender), ", ".join(map(format_mail, receivers)))

try:
    print("sending message: " + message)
    with smtplib.SMTP_SSL('smtp.example.com', 465) as session:
        session.login("sender@example.com", "sender_password")
        session.sendmail(sender, receivers, message)
    print("message sent")
except smtplib.SMTPException:
    print("could not send mail")

The only thing that changed from simple.py is that we are now getting an instance of SMTP_SSL pointing to the default SMTP SSL port (465), and we added a line which invokes SMTP_SSL.login(user, password).

This example was tested with the GMail SMTP server, via SSL, with my own credentials and worked just fine.

3. Sending HTML

Remember how I talked about the headers required for the message to be compliant with the SMTP protocol? Well, as those are the ones required, there are other headers which can be sent that can let us do some pretty awesome stuff.

By using the headers MIME-Version and Content-type we can tell the mail client how to interpret the information we are sending. We’ll now see how to set these headers to send HTML in a mail, but keep in mind that this is not just as easy as writing HTML in a browser, there are so so so… so many mail clients out there, that writing HTML that is compliant to every one of them is a pretty defying task.

html_mail.py

import smtplib


def format_mail(mail):
    return "{} ".format(mail.split("@")[0], mail)

smtp_server_host = "smtp.example.com"
smtp_server_port = 465
sender = "sender@example.com"
pwd = "sender_password"
receivers = ["receiver@example.com"]
message = """
<h1>This is a title</h1>
<h2>This is a sub title</h2>
<p>This is a paragraph <strong>with some bold text</strong>.</p>
"""

formatted_message = """From: {}
To: {}
MIME-Version: 1.0
Content-type: text/html
Subject: Example Subject

{}
""".format("{} ".format(sender.split("@")[0], sender), ", ".join(map(format_mail, receivers)), message)

try:
    print("sending message: " + message)
    with smtplib.SMTP_SSL(smtp_server_host, smtp_server_port) as session:
        session.login(sender, pwd)
        session.sendmail(sender, receivers, formatted_message)
    print("message sent")
except smtplib.SMTPException:
    print("could not send mail")

As you see, the actual code for connecting to the SMTP server, logging in and sending the message is the same as before. The only thing we actually changed was the headers of the message. By setting MIME-Version: 1.0 and Content-type: text/html, the mail client will now know that this is HTML and should be rendered as such.

4. Sending Attachments

To send a mail with mixed content you need to send the Content-type header to multipart/mixed and then, text and attachment sections can be specified within boundaries. A boundary is started with two hyphens followed by a unique string, which cannot appear in the message part of the e-mail. A final boundary denoting the e-mail’s final section must also end with two hyphens.

We’ll now send a mail with HTML in the body and an attachment. It will be a text file containing the following piece of Lorem Ipsum:

attachment.txt

“Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam eu justo vel tortor hendrerit dignissim et sit amet arcu. Pellentesque in sapien ipsum. Donec vitae neque blandit, placerat leo ac, tincidunt libero. Donec ac sem ut libero volutpat facilisis sit amet ac ante. Interdum et malesuada fames ac ante ipsum primis in faucibus. Duis quis elit porta, bibendum lacus vel, auctor sem. Nulla ut bibendum ipsum. In efficitur mauris sed interdum commodo. Maecenas enim orci, vestibulum et dui id, pretium vestibulum purus. Etiam semper dui ante, convallis volutpat massa convallis ut. Pellentesque at enim quis est bibendum eleifend sit amet eget enim.

Morbi cursus ex ut orci semper viverra. Aenean ornare erat justo. Cras interdum mauris eu mauris aliquet tincidunt. Praesent semper non tellus a vehicula. Suspendisse potenti. Sed blandit tempus quam. In sem massa, volutpat nec augue eu, commodo tempus metus. Fusce laoreet, nunc in bibendum placerat, sem ex malesuada est, nec dictum ex diam nec augue. Aliquam auctor fringilla nulla, vitae laoreet eros laoreet ut. Sed consectetur semper risus non efficitur. Nunc pharetra rhoncus consectetur.

Nam dictum porta velit sit amet ultricies. Praesent eu est vel ex pretium mattis sit amet a ex. Mauris elit est, eleifend et interdum nec, porttitor quis turpis. Sed nisl ligula, tempus ac eleifend nec, faucibus at massa. Nam euismod quam a diam iaculis, luctus convallis neque sollicitudin. Etiam eget blandit magna, non posuere nisi. Aliquam imperdiet, eros nec vestibulum pellentesque, ante dolor tempor libero, in efficitur leo lectus eu ex. Pellentesque sed posuere justo. Etiam vestibulum, urna at varius faucibus, odio eros aliquet tortor, nec rhoncus magna massa eu felis. Phasellus in nulla diam.

Pellentesque blandit sapien orci, sit amet facilisis elit commodo quis. Quisque euismod imperdiet mi eu ultricies. Nunc quis pellentesque felis, aliquam ultrices neque. Duis quis enim non purus viverra molestie eget porttitor orci. Morbi ligula magna, lacinia pulvinar dolor at, vehicula iaculis felis. Sed posuere eget purus sed pharetra. Fusce commodo enim sed nisl mollis, eu pulvinar ligula rutrum.

In mattis posuere fringilla. Mauris bibendum magna volutpat arcu mollis, nec semper lorem tincidunt. Aenean dictum feugiat justo id condimentum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi lobortis, ipsum et condimentum cursus, risus nunc porta enim, nec ultrices velit libero eget dui. Nulla consequat id mi nec hendrerit. Suspendisse et odio at mauris viverra pellentesque. Nunc eget congue nisi.”

Here goes the python code to send this file attached to a mail with HTML:

attachment_html_mail.py

import smtplib


def format_mail(mail):
    return "{} ".format(mail.split("@")[0], mail)


def message_template(sender, receivers, subject, boundary, body, file_name, attachment):
    return """From: {}
To: {}
Subject: {}
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary={}
--{}
Content-Type: text/html
Content-Transfer-Encoding:8bit

{}
--{}
Content-Type: multipart/mixed; name="{}"
Content-Transfer-Encoding:base64
Content-Disposition: attachment; filename={}

{}
--{}--
""".format(format_mail(sender), ", ".join(map(format_mail, receivers)), subject, boundary, boundary, body,
           boundary, file_name, file_name, attachment, boundary)


def main():
    sender = "sender@example.com"
    receivers = ["receiver@example.com"]
    subject = "Test Mail with Attachment and HTML"
    boundary = "A_BOUNDARY"
    msg_body = """
    <h1>Hello there!</h1>
    <p>You will find <strong>attachment.txt</strong> attached to this mail. <strong>Read it pls!</strong></p>
    """
    file_name = "attachment.txt"
    attachment = open(file_name, "rb").read().decode()

    smtp_server_host = "smtp.example.com"
    smtp_server_port = 465
    pwd = "sender_password"

    message = message_template(sender, receivers, subject, boundary, msg_body, file_name, attachment)

    try:
        with smtplib.SMTP_SSL(smtp_server_host, smtp_server_port) as session:
            session.login(sender, pwd)
            session.sendmail(sender, receivers, message)
        print("mail was successfully sent")
    except smtplib.SMTPException:
        print("could not send mail")


if __name__ == '__main__':
    main()

Again, the process of connecting to the SMTP server, logging in and sending the mail is untouched. The magic is happening in the message, with its headers and content.

The first headers define From, To, Subject, MIME-Version, Content-Type and, with it, boundary. These are the headers for the whole message.

Then, after using the boundary to separate the definitions of the global headers from the body of the message, we are defining the Content-Type and the Content-Transfer-Encoding for the body of the mail only, and then we insert the body of the message.

Separated from that last section, we are defining the Content-Type, with the name (of the attachment), the Content-Transfer-Encoding and the Content-Disposition with the filename, and then we insert the file content. Then we just end the mail with two hyphens.

5. Download the Code Project

This was a basic example on how to send mails with Python’s smtplib module.

Download
You can download the full source code of this example here: python-mail

Sebastian Vinci

Sebastian is a full stack programmer, who has strong experience in Java and Scala enterprise web applications. He is currently studying Computers Science in UBA (University of Buenos Aires) and working a full time job at a .com company as a Semi-Senior developer, involving architectural design, implementation and monitoring. He also worked in automating processes (such as data base backups, building, deploying and monitoring applications).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button