In our last tutorial, we showed you how to send emails through a Gmail server using a Python script. In this tutorial, you’ll learn how to access emails in your Gmail account via a Python script. We’ll use Python’s imap_tools library to automatically read emails and even download attachments from your Gmail account.

Generating Gmail App Password for Python

We discussed this at length in our last tutorial, but it’s worth mentioning again. You can’t use your Gmail password to access your Gmail account via a Python application. Instead, you need to generate a special app password that you’ll use to get into your Gmail account from within a Python application. The process to generate a Gmail App Password has been explained in detail in our last tutorial Basically, there are two steps involved:

  1. To securely generate an app password, you need to enable 2-step verification for your Gmail account. To enable two step verification, login to your Gmail account and click this link.

  2. After enabling two-step verification, you can generate app passwords. The steps for creating application password are explained here.

Once you have an app password, save it. You’ll need this password to log in to your Gmail account via a Python application, which we’re going to do right now.

Receiving Emails via a Python Application

To access emails from your Gmail account, you need your Gmail Id and the app password you generated in the previous step.

Let’s first enter your email address. The following script asks users to enter their email address in a hidden string. For the safety of your account, it’s always a good practice to enter emails and passwords in the form of hidden strings like this. To capture user input in a hidden format, you can use the getpass() method of the “getpass” module as shown below:

import getpass
my_email = my_pass = getpass.getpass("Enter your email:")

Output:

Enter your email:········

Once you enter your email address, the next step is to enter the app password you just created. Execute the following script and enter your app password.

my_pass = getpass.getpass("Enter your app password:")

Output:

Enter your app password:········

Logging into your Gmail Account

You’ll be using Pythons imap_tools module to access the Gmail server and to login to your account. To download the imap_tools library, execute the following command on your command terminal.


$ pip install imap_tools

The following Python script shows how to connect and login to your Gmail account. The email and password are passed to the login() method of the Mailbox class.

from imap_tools import MailBox

mailbox =  MailBox('imap.gmail.com').login(my_email, my_pass )

Reading Emails

To access specific emails, you need to call the fetch() method from your Mailbox class object. The first parameter for the fetch() method is the search query string that filters your email. For example, the following script returns all the emails from your Gmail account containing a keyword odesk. The fetch() method returns a generator containing information about all the emails that satisfy the criteria defined by the search query.

You can iterate through all the emails and print a variety of information about the email using different attributes. For instance, the following script prints the email IDs, email subjects, and the date you received the emails.

for msg in mailbox.fetch('FROM "odesk"', charset='utf8'):
    print("Message id:",msg.uid)
    print("Message Subject:",msg.subject)
    print("Message Date:", msg.date)
    print("=============")

Here is a sample output of the above script.

Output:

Message id: 596
Message Subject: Welcome to oDesk, XYZ!
Message Date: 2015-03-11 05:10:54+00:00
=============
Message id: 601
Message Subject: Upcoming freelancer webinar: earning more on oDesk
Message Date: 2015-03-13 06:02:01-07:00
=============
Message id: 607
Message Subject: Pssst...can you keep a secret, XYZ?
Message Date: 2015-03-16 07:04:51-07:00
=============
Message id: 618
Message Subject: Offer: Need a 500-600 word blog post on JavaScript Promises (highly
 technical) in 48 hours
Message Date: 2015-03-23 01:20:30+00:00
=============

Get Our Python Developer Kit for Free

I put together a Python Developer Kit with over 100 pre-built Python scripts covering data structures, Pandas, NumPy, Seaborn, machine learning, file processing, web scraping and a whole lot more - and I want you to have it for free. Enter your email address below and I'll send a copy your way.

Yes, I'll take a free Python Developer Kit

Details of search criteria supported by imap_tools library are described in the following table:

Key Types Results Description
answered bool ANSWERED Opposite is UNANSWERED with|without the Answered flag
seen bool SEEN Opposite is UNSEEN with|without the Seen flag
flagged bool FLAGGED Opposite is UNFLAGGED with|without the Flagged flag
draft bool DRAFT Opposite is UNDRAFT with|without the Draft flag
deleted bool DELETED Opposite is UNDELETED with|without the Deleted flag
keyword str* KEYWORD KEY with the specified keyword flag
no_keyword str* UNKEYWORD KEY without the specified keyword flag
from_ str* FROM "from@ya.ru" contain specified str in envelope struct's FROM field
to str* TO "to@ya.ru" contain specified str in envelope struct's TO field
subject str* SUBJECT "hello" contain specified str in envelope struct's SUBJECT field
body str* BODY "some_key" contain specified str in body of the message
text str* TEXT "some_key" contain specified str in header or body of the message
bcc str* BCC "bcc@ya.ru" contain specified str in envelope struct's BCC field
cc str* CC "cc@ya.ru" contain specified str in envelope struct's CC field
date datetime.date* ON 15-Mar-2000 internal date is within specified date
date_gte datetime.date* SINCE 15-Mar-2000 internal date is within or later than the specified date
date_lt datetime.date* BEFORE 15-Mar-2000 internal date is earlier than the specified date
sent_date datetime.date* SENTON 15-Mar-2000 rfc2822 Date: header is within the specified date
sent_date_gte datetime.date* SENTSINCE 15-Mar-2000 rfc2822 Date: header is within or later than the specified date
sent_date_lt datetime.date* SENTBEFORE 1-Mar-2000 rfc2822 Date: header is earlier than the specified date
size_gt int >= 0 LARGER 1024 rfc2822 size larger than specified number of octets
size_lt int >= 0 SMALLER 512 rfc2822 size smaller than specified number of octets
new True NEW have the Recent flag set but not the Seen flag
old True OLD do not have the Recent flag set
recent True RECENT have the Recent flag set
all True ALL all, criteria by default
uid iter(str)/str/U UID 1,2,17 corresponding to the specified unique identifier set
header H(str, str)* HEADER "A-Spam" "5.8" have a header that contains the specified str in the text
gmail_label str* X-GM-LABELS "label1" have this gmail label.

For more details about search criteria, check out the official documentation.

Let’s see another example demonstrating how to search an email with a particular subject line. To do so, you need to pass the string SUBJECT "Your subject line" to the fetch() method of the Mailbox class. The following script accesses the email with the subject mobile_phones_list. The script then displays the id, subject, text and receive date of the email.

for msg in mailbox.fetch('SUBJECT "mobile_phones_list"', charset='utf8'):
    print("Message id:",msg.uid)
    print("Message Subject:",msg.subject)
    print("Message Text:",msg.text)
    print("Message Date:", msg.date)

Here is the output.

Output:

Message id: 13587
Message Subject: mobile_phones_list
Message Text: Here is a list of some famous mobile phones. please find the attachment.

Downloading Attachments from Email

You can also access and download attachments from your emails. To do so, you can use the attachment attribute of the email. The following script prints the name and payload of the attachment in your email. The “payload” attribute actually contains the file contents in byte format.

for msg in mailbox.fetch('SUBJECT "mobile_phones_list"', charset='utf8'):
    for att in msg.attachments:
        print(att.filename)
        print(att.payload)

Output:

Here is the output:

mobile_lists.jpg
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\x03\x02\x02\x03\x02\x02\x03\x03\x03\x03\x04\x03\x03\x04\x05\x08\x05

The above output shows the file name, which is an image and the byte string for the file.

You can convert the bytes string containing your file content to an image file using the Image class. Look at the following script:

from imap_tools import MailBox
import getpass
import os
import io
import PIL.Image as Image
my_email = getpass.getpass("Enter your email:")
my_pass = getpass.getpass("Enter your app password:")
mailbox =  MailBox('imap.gmail.com').login(my_email, my_pass )
for msg in mailbox.fetch('SUBJECT "mobile_phones_list"', charset='utf8'):
    for att in msg.attachments:
        print(att.filename)
        bytes = att.payload
        image = Image.open(io.BytesIO(bytes))
        image.save(r"C:/"+att.filename)

Update the path where you want to save the image in the last line, then once you run the script, you’ll see the email attachment will be succefully downloaded to your local system.

Output:

mobile_lists.jpg

Finding a way to convert byte strings to a file like this is key to downloading Gmail attachments using Python. If you found this tutorial valuable, please subscribe using the form below so we can share other short, powerful Python tips we’ve learned through the years.


Get Our Python Developer Kit for Free

I put together a Python Developer Kit with over 100 pre-built Python scripts covering data structures, Pandas, NumPy, Seaborn, machine learning, file processing, web scraping and a whole lot more - and I want you to have it for free. Enter your email address below and I'll send a copy your way.

Yes, I'll take a free Python Developer Kit