×

Send & Read E-Mail in Python

Generally, to send emails we use an SMTP server and to view emails we use IMAP servers. Python Provides In-built Modules to Send and Read emails, to send emails we use the SMPTlib module, and to read the emails we use the IMAPlib module.

SMTP server

SMTP Servers are responsible for sending emails and they implement SMTP protocols. Simple Mail Transfer protocol also known as SMTP is a simple but efficient protocol that delivers email messages from sender to receiver.

When we would like to send an email message we fill up the email draft with the recipient address, subject, message body, etc., and click the send button, this sends the email to the smtp server.

Smtp Server

Now the Host_Name of the SMTP Server to which the email is sent is based on the type of account the user uses. There are different email service providers such as Gmail, yahoo, outlook, hostinger, etc.,

  • If the account is a gmail account then the host name will be smtp.gmail.com and if an account is hostinger then the address will be smtp.hostinger.com
  • These SMTP servers have their respective port numbers

Thus when we click send button, first the email is sent to the SMTP server of the sender and it is then sent to the address of the respective recipient’s SMTP server and stored on it till the receiver logs into their account.

Smtp Handshake 1

SMTPlib Library

In python, we can automate the process of sending bulk emails to particular recipients using smtplib library. SMTPlib provides inbuilt methods to perform login/logout and send emails.

To send an email to a recipient there are a few steps we need to perform and they are listed below:-

  • Establish a Connection with Sender’s SMTP Server using SMTP Server host_name and port number
  • Verify yourself as a valid user
  • Login using username & password
  • Create a message
  • If the message is of text format we can directly send it as a text. If the message is in other formats such as pdf, audio, video format then we need to encode the data and send
  • Send message to Recipient email address
  • Abort the connection if required

Connect to an SMTP Server

To connect to an SMTP Server we need to create an smtp session object that connects to the server. This session object will be used to send emails. To create an smtp session object we need to pass the Hostname and Port Number to smtplib.SMTP()

smtplib.SMTP() create an instance that encapsulates the SMTP connection and also it has methods that provide support for different operations

Host Name & Port Number

To establish a connection with any SMTP Server we need a hostname and port number. The Hostname and port number are different for various SMTP Servers. The most common SMTP Servers we use in daily life along with their Hostname and Port number are listed below:-

  • Gmail SMTP Server – – – Hostname : smtp.gmail.com , Port : 465 (SSL), 587 (TLS)
  • Hostinger SMTP Server – – – Hostname : smtp.hostinger.com , Port : 465 (SSL/TLS)
  • Outlook SMTP Server – – – Hostname : smtp.office365.com , Port : 587 (TLS)
  • Yahoo SMTP Server – – – Hostname : smtp.yahoo.com , Port : 465 (SSL), 587 (TLS)

We need to pass these hostname and port numbers to the session object to connect to an smtp server. Since we are using Gmail we need to pass hostname: smtp.gmail.com & port number: 587

import smtplib
session = smtplib.SMTP('smtp.gmail.com', 587)

We can also create the session object using with statement.

import smtplib
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:

smtp.ehlo()

EHLO is known as Extended HELO. smtp.ehlo() allows the client to identify itself to the SMTP server. When we make a handshake using ehlo(), the server offers more commands and extensions when compared to helo(). In case the server could not support ehlo then the client falls back to the helo protocol.

To initiate an ehlo() handshake we need to use the below command:-

smtp.ehlo()

smtp.starttls()

Start TLS is a protocol that the client mentions the server to offer a more secured connection from an insecure connection. It is mandatory to use when we make an ehlo() handshake with the server.

In python, we can initiate a TLS protocol using smtp.starttls(). In case we would like to implement SSL protocol then we need to create the session object using smtplib.SMTP_SSL(). When we use SSL protocol we don’t need to specify a ehlo() handshake.

The Syntax for both protocol’s implementation is given below:-

# EHLO() with TLS()
import smtplib
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
    smtp.ehlo()
    smtp.starttls()
    smtp.ehlo()

# SSL() 
import smtplib
with smtplib.SMTP_SSL('smtp.gmail.com', 587) as smtp:

smtp.login()

Once we establish a secured connection with the server, we need to log in using the email_id and password. In python, while logging in using the smtp server, we need to use an application-specific password instead of the general account password that we use.

The application-specific password is generated when we register the device that we are using to perform the send-email operation. This password is different for other devices that use the same account to log in.

To generate an application-specific password while using the google smtp server please follow the below steps:-

  • Open the Google account home page and select the security section. Google Account – > Security
Security 2
  • Scroll down to Signing into Google and turn on the 2-step verification in case if it’s switched off
2 Step Verification 1
  • Under 2-step verification, we can click the App passwords and generate a 16-character code. This will be used to login to our account and send emails
App Passwords
import smtplib
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
    smtp.ehlo()
    smtp.starttls()
    smtp.ehlo()
    smtp.login(user="[email protected]", password="ostf********wqhk")

smtp.sendmail()

To send an email we can use smtp.sendmail(), we need to pass the sender and recipient’s email address along with the message as arguments.

Over here, we send the message details such as subject and body by wrapping them in a string format.

import smtplib
with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
    smtp.ehlo()
    smtp.starttls()
    smtp.ehlo()
    smtp.login(user="[email protected]", password="ostf********wqhk")
    subject = 'hello'
    body = 'welcome user'
    msg = f'Subject: {subject} \n\n {body}'
    smtp.sendmail(from_addr='[email protected]', 
                     to_addrs='[email protected]', msg=msg)

Output:-

Send Email

MIMEMultipart

MIME stands for multipurpose internet mail extensions. It allows us to send email messages in different formats such as audio, video, PDFs, etc.,

MIMEMultipart lets us send multiple parts( files ) by listing them one after the other. These files are basically attachments. MIMEMultipart is a sub-class of MIMEBase.

We can create a MIMEMulitpart object and use this object to define the parameters such as from _address, to_address, message content, etc.,

from email.mime.multipart import MIMEMultipart
import smtplib

msg = MIMEMultipart()
msg['from'] = '[email protected]'
msg['to'] = '[email protected]'
msg['subject'] = 'Read New'

Now that we have created the message object and assigned a few attributes to it, we can add the message content in the next step. To perform this operation we use MIMEBase class.

MIMEBase

MIMEBase is the base class for all the message sub-classes. It allows defining the message type using two parameters _maintype and _subtype and add the message payload and header.

let’s see an example for sending an image file using MIME by implementing MIMEMultipart and MIMEBase.

Sending Image files

To send an image we instantiate the MIMEMultipart() and assign the field values to it, such as message, sender_address, recipient_address, etc., and attach the image to the object( msg ) of MIMEMultipart. We need to convert the image data into bytes and then attach the data to the msg object.

When we attach media files or documents to the message object we need to specify a few parameters about the data and encode it. Below are the steps to follow:-

  • Opening the file in ‘rb’ mode, so that the data is read in bytes format
  • assign the data and file_name to respective attributes
  • Define the _maintype and _subtype for the data and pass them to MIMEBase(). In our case image is the _maintype & .png is the _subtype. To know the _subtype we are using imghdr.what()
  • Next we need to add header and payload to the instance of the MIMEBase(). Once the data is properly channeled we can attach it to the message object and send the email

In the below code we are sending the image file in the format of the byte from a sender address to the recipient address.

from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
import smtplib
from email import encoders
import imghdr

msg = MIMEMultipart()
msg['from'] = '[email protected]'
msg['to'] = '[email protected]'
msg['subject'] = 'Send Image'

with open('lemon_splash.jpg', 'rb') as file:
    img_data = file.read()
    img_type = imghdr.what(file.name)
    file_name = file.name
img_part = MIMEBase(_maintype='image', _subtype=img_type)
img_part.set_payload(img_data)
encoders.encode_base64(img_part)
img_part.add_header(
    "Content-Disposition",
    f"attachment; filename= {file_name}",
)
msg.attach(img_part)

server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(user="[email protected]", password="ostf********wqhk")
server.send_message(msg)

Output:-

Smtp Send Image 3
Send Image Details

Attach PDF to message

The process to attach PDFs to messages is almost the same as we have done in the previous step. Except we need to pass different parameters to the MIMEBase().

We first open the file in reading + binary mode and extract the filename and the data in the form of bytes. Then we upload the extracted bytes using payload and add header before attaching the data to the message.

Since the type of file we are sending is a pdf format, it needs to be opened with pdf supported application. So we specify this by assigning _maintype = 'application', _subtype = 'octet-stream'

from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders

msg = MIMEMultipart('alternative')
msg['from'] = '[email protected]'
msg['to'] = '[email protected]'
msg['subject'] = 'Read New'
with open('document.pdf', 'rb') as file:
    doc_data = file.read()
    doc_name = file.name
doc_part = MIMEBase(_maintype='application', _subtype='octet-stream')
doc_part.set_payload(doc_data)
encoders.encode_base64(doc_part)
doc_part.add_header(
    "Content-Disposition",
    f"attachment; filename= {doc_name}",
)
msg.attach(doc_part)

Attach Audio to message

To attach an audio file to the MIMEMultipart msg object, we need to open the file in 'rb' mode to extract the data in bytes format and pass it as a payload.

Since the file is in the audio format we assign _maintype = 'audio', _subtype = 'ogg' based on file type and pass these values to the MIMEBase.

from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders

msg = MIMEMultipart()
with open('Audio_1.ogg', 'rb') as file:
    Audio_data = file.read()
    Audio_name = file.name
Audio_part = MIMEBase(_maintype='audio', _subtype='ogg')
Audio_part.set_payload(Audio_data)
encoders.encode_base64(Audio_part)
Audio_part.add_header(
    "Content-Disposition",
    f"attachment; filename= {Audio_name}",
)
msg.attach(Audio_part)

Attach HTML Content

To Attach HTML Content to a message object we can use MIMEText(). We can directly pass the HTML content to MIMEText(), and it can be attached to the message object using msg.attach().

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

msg = MIMEMultipart()
msg['from'] = '[email protected]'
msg['to'] = '[email protected]'
msg['subject'] = 'HTML Message'

html_part = MIMEText('<h1 style="color:red; ; font-size:150px;"> Hello Varun </h1>', 'html')
msg.attach(html_part)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(user="[email protected]", password="ostf********wqhk")
server.send_message(msg)

Output:-

Html Message

EmailMessage

Apart from MIMEMultipart we can also instantiate the EmailMessage() class and attach messages. In EmailMessage we don’t have to encode the data, we can pass it just by specifying the _maintype and _subtype of the data.

To instantiate the EmailMessage() class we need to import it using the command, from email.message import EmailMessage.

In the below example, we are sending an image file to the recipient address using the instance of EmailMessage().

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

msg = EmailMessage()
msg['from'] = '[email protected]'
msg['to'] = '[email protected]'
msg['subject'] = 'Read New Message'
with open('lemon_splash.jpg', 'rb') as file:
    img = file.read()
    img_type = imghdr.what(file.name)
    file_name = file.name
msg.add_attachment(img, maintype='image', subtype=img_type, filename=file_name)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.login(user="[email protected]", password="ostf********wqhk")
server.send_message(msg)

Output:-

Emailmessage
Emailmessage Address

IMAP & POP Server

IMAP & POP are email servers that handle incoming emails. We can either use IMAP or POP based on requirement specifications.

IMAP and POP server stores the incoming emails and these can be retrieved by the user when logged in from a local machine.

Imap Pop Servers 2

IMAP Server

IMAP stands for INTERNET MAIL ACCESS PROTOCOL. Generally, when we receive emails they are stored on the relay server. These emails are retrieved by the user when they log in from a local machine.

IMAP allows the user to view or delete the emails on the server. IMAP just creates a local cache for those incoming emails on the local machine, the original emails are stored on the relay server itself.

When logged in from multiple devices, IMAP displays similar content such as folder, unread messages, trash, etc.,

Imap Server

POP Server

POP means POST OFFICE PROTOCOL. POP is used to download the email from the recipient’s email server.

Unlike IMAP, instead of viewing it directly downloads the email to the local machine. Once the email is downloaded it is removed from the server’s hard disk. POP should be used only to download the emails from the server.

Also when users log in from multiple devices after the first device downloads an email it will be removed from the server thus the email will not be available to the other device, since POP does not provide synchronization.

Pop Server 1

In the above image, two devices have a login from the same account and we can conclude that incoming emails for multiple device login are different on each device.

IMAP vs POP

  • We can view the incoming emails using IMAP
  • IMAP caches local copies of the email on your device and the actual emails are stored on server itself
  • All the email and folders are properly Synchronized among multiple devices
  • We cannot view the email without internet connection
  • Consumes more space on the server
  • More secure compared to POP
  • POP downloads the whole email from the server
  • Once downloaded the email will be deleted from server, thus we need a backup plan on a local device
  • Doesnot provide Synchronization. The interface and folders could be different on multiple devices
  • Email can be viewed without internet connection
  • Consume less space on Server
  • Less secure because the downloaded email can contain viruses

IMAPlib

Python provides an in-built module imaplib to view emails on an IMAP server. Generally, we can view the inbox, sent items, drafts, other folders that are existing on the server for a particular user account.

Using this imaplib module we can log in, log out, read messages and print them. First, let’s check out how to establish a secured connection with the server.

The points listed below are helpful for establishing a secure connection with the server. We can choose either SSL or TLS encryption standards.

  • Create a IMAP session object for connecting to an imap server of a user account. To connect to a server we need host name and port number
  • The host name for a server would be imap.server_name.com, For example:- Gmail IMAP server host name would be : imap.gmail.com
  • The Port Number for encrypted TLS/SSL : 993 & for Unecrypted : 143
  • Next we need to log in to the account using email_id and password. The password will be application password
  • Next we can select the folder we would like to open. Such as ‘inbox’, ‘sent’, ‘spam’, etc.,
  • While fetching the messages we need to specify in which format we would like to extract the emails. In our example we use standard 'RFC822' format
  • Since the email message will be in binary format we can use email.message_from_bytes() to extract data in binary format to string format. Finally, Before retrieving the message we need to decode it

In the below code, we are viewing and printing the ‘UNSEEN’ messages from ‘inbox’,

import email
import imaplib
server = imaplib.IMAP4_SSL('imap.gmail.com', 993)
server.login(user="[email protected]", password="ostf********wqhk")
server.select('inbox')
_, inbox = server.search(None, 'UNSEEN')

for id in inbox[0].split():
    _, data = server.fetch(id, '(RFC822)')
    _, binary_data = data[0]
    email_message = email.message_from_bytes(binary_data)
    email_data = {}

    for i in ['subject', 'from', 'to', 'date']:
        print(i, ':', '', email_message[i])

    for part in email_message.walk():
        if part.get_content_type() == 'text/plain':
            body = part.get_payload(decode=True)
            email_data['body'] = body.decode()
        
    print('Message Body : ', email_data['body'])

Output:-

Imap Message