Friday, January 4, 2019

imapclient module - Retrieving and Deleting Emails

The Internet Message Access Protocol (IMAP) specifies how to communicate with an email provider’s server to retrieve emails sent to your email address. IMAP is an email retrieval protocol which does not download the emails. It just reads them and displays them. It enables us to take any action such as downloading, delete the mail without reading the mail.It enables us to create, manipulate and delete remote message folders called mail boxes. IMAP uses following commands to handle emails:

1 IMAP_LOGIN - This command opens the connection.
2 CAPABILITY - This command requests for listing the capabilities that the server supports.
3 NOOP - This command is used as a periodic poll for new messages or message status updates                             during a period of inactivity.
4 SELECT - This command helps to select a mailbox to access the messages.
5 EXAMINE - It is same as SELECT command except no change to the mailbox is permitted.
6 CREATE - It is used to create mailbox with a specified name.
7 DELETE - It is used to permanently delete a mailbox with a given name.
8 RENAME - It is used to change the name of a mailbox.
9 LOGOUT - This command informs the server that client is done with the session. The server must send BYE untagged response before the OK response and then close the network connection.


Python’s client side library called imaplib is used for accessing emails over imap protocol. The following example shows how to read messages from a Gmail account:

import imaplib
import pprint

imap_host = 'imap.gmail.com'
imap_user = 'vivek@gmail.com'
imap_pass = '123456789'

# connect to host using SSL
imap = imaplib.IMAP4_SSL(imap_host)

## login to server
imap.login(imap_user, imap_pass)

imap.select('Inbox')

tmp, data = imap.search(None, 'ALL')
for num in data[0].split():
tmp, data = imap.fetch(num, '(RFC822)')
print('Message: {0}\n'.format(num))
pprint.pprint(data[0][1])
break
imap.close()

In the above example we first login to a gmail server with user credentials. Next we display the messages in the inbox. A for loop is used to display the fetched messages one by one and finally the connection is closed.

Although Python comes with an imaplib module, but the third-party imapclient module is easier to use. The imapclient module downloads emails from an IMAP server then the pyzmail module does the parsing of these email messages into simple string values. So lets install imapclient and pyzmail modules first from command prompt.

We'll use:

1. pip install imapclient to install the imapclient. After running this command we get the following output:

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Users\Python>pip install imapclient
Collecting imapclient
  Downloading https://files.pythonhosted.org/packages/dc/39/e1c2c2c6e2356ab6ea81
fcfc0a74b044b311d6a91a45300811d9a6077ef7/IMAPClient-2.1.0-py2.py3-none-any.whl (
73kB)
    100% |████████████████████████████████| 81kB 1.2MB/s
Requirement already satisfied: six in c:\users\python\appdata\roaming\python\pyt
hon36\site-packages (from imapclient) (1.11.0)
Installing collected packages: imapclient
Successfully installed imapclient-2.1.0

C:\Users\Python>

2. pip install pyzmail36 to install the pyzmail as pip install pyzmail gave error as shown below:

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Users\Python>pip install pyzmail
Collecting pyzmail
  Using cached https://files.pythonhosted.org/packages/23/9a/c8709821fa15cec44f8
25fba884284b261a06d8a0033a16f4c35470eb26a/pyzmail-1.0.3.tar.gz
Collecting distribute (from pyzmail)
  Using cached https://files.pythonhosted.org/packages/5f/ad/1fde06877a8d7d5c9b6
0eff7de2d452f639916ae1d48f0b8f97bf97e570a/distribute-0.7.3.zip
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\Python\AppData\Local\Temp\pip-install-9sbeo5m8\distribute\s
etuptools\__init__.py", line 2, in <module>
        from setuptools.extension import Extension, Library
      File "C:\Users\Python\AppData\Local\Temp\pip-install-9sbeo5m8\distribute\s
etuptools\extension.py", line 5, in <module>
        from setuptools.dist import _get_unpatched
      File "C:\Users\Python\AppData\Local\Temp\pip-install-9sbeo5m8\distribute\s
etuptools\dist.py", line 7, in <module>
        from setuptools.command.install import install
      File "C:\Users\Python\AppData\Local\Temp\pip-install-9sbeo5m8\distribute\s
etuptools\command\__init__.py", line 8, in <module>
        from setuptools.command import install_scripts
      File "C:\Users\Python\AppData\Local\Temp\pip-install-9sbeo5m8\distribute\s
etuptools\command\install_scripts.py", line 3, in <module>
        from pkg_resources import Distribution, PathMetadata, ensure_directory
      File "C:\Users\Python\AppData\Local\Temp\pip-install-9sbeo5m8\distribute\p
kg_resources.py", line 1518, in <module>
        register_loader_type(importlib_bootstrap.SourceFileLoader, DefaultProvid
er)
    AttributeError: module 'importlib._bootstrap' has no attribute 'SourceFileLo
ader'

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in C:\Users\Python\A
ppData\Local\Temp\pip-install-9sbeo5m8\distribute\

C:\Users\Python>pip install pyzmail36
Collecting pyzmail36
  Downloading https://files.pythonhosted.org/packages/8c/28/34f999ca9704f7b68976
fb82f05b49a09f0bb3902560572dbfbcd42c1fcc/pyzmail36-1.0.4.tar.gz (47kB)
    100% |████████████████████████████████| 51kB 799kB/s
Requirement already satisfied: setuptools in c:\users\python\appdata\local\progr
ams\python\python36\lib\site-packages (from pyzmail36) (28.8.0)
Installing collected packages: pyzmail36
  Running setup.py install for pyzmail36 ... done
Successfully installed pyzmail36-1.0.4

C:\Users\Python>

Now that we have all the required modules let's make a program for logging in to an IMAP server,
searching for emails, fetching them, and then extracting the text of the email messages from them. See the following code:

import imapclient
import pyzmail

imapObj = imapclient.IMAPClient('imap.covrisolutions.com', ssl=True)

imapObj.login('vivek@covrisolutions.com', '123456789')

imapObj.select_folder('INBOX', readonly=True)

UIDs = imapObj.search(['SINCE 04-Jan-2019'])

rawMessages = imapObj.fetch(UIDs[0], ['BODY[]', 'FLAGS'])

message = pyzmail.PyzMessage.factory(rawMessages[UIDs[0]]['BODY[]'])

print(message.get_subject())
print(message.get_addresses('from'))
print(message.get_addresses('to'))
imapObj.logout()

When we run this program we get the following output:

Re:Project updates
[(A Satya', 'satya@covrisolutions.com')]
[('Vivek Swami', 'vivek@covrisolutions.com')]


------------------
(program exited with code: 1)

Press any key to continue . . .

Re:Project updates is the subject of email in my account. Next two outputs show sender and receiver email IDs.

We need an IMAPClient object to connect to an IMAP server and receive email. First we’ll need the domain name of our email provider’s IMAP server which in my case is is  imap.covrisolutions.com then call the imapclient.IMAPClient() function to create an IMAPClient object. As most email providers require SSL encryption, we pass the ssl=True keyword argument. Now we use the client object and call its login() method, passing in the username (this is usually your email address) and password as strings. If the IMAP server rejects this username/password combination, Python will raise an imaplib.error exception.

The retrieval of an email is a two-step process. First, we must select a folder we want to search through, say INBOX. Then, we must call the IMAPClient object’s search() method, passing in a string of IMAP search keywords. If the selected folder does not exist, Python will raise an imaplib.error exception. The readonly=True keyword argument prevents you from accidentally making changes or deletions to any of the emails in this folder during the subsequent method calls. Unless you want to delete emails, it’s a good idea to always set readonly to True.

After a folder is selected, we can now search for emails with the IMAPClient object’s search() method. The argument to search() is a list of strings, each formatted to the IMAP’s search keys. The search keys are :

'ALL', 'BEFORE date', 'ON date', 'SINCE date', 'SUBJECT string', 'BODY string', 'TEXT string', 'FROM string','TO string','CC string','BCC string', 'SEEN', 'UNSEEN', 'ANSWERED', 'UNANSWERED', 'DELETED','UNDELETED','DRAFT','UNDRAFT', 'FLAGGED', 'UNFLAGGED', 'LARGER N', 'SMALLER N', 'NOT search-key', 'OR search-key1 search-key2'.

We can pass multiple IMAP search key strings in the list argument to the search() method. The messages returned are the ones that match all the search keys. If you want to match any of the search keys, use the OR search key. For the NOT and OR search keys, one and two complete search keys follow the NOT and OR, respectively.

The search() method doesn’t return the emails themselves but rather unique IDs (UIDs) for the emails, as integer values. You can then pass these UIDs to the fetch() method to obtain the email content.

If your search matches a large number of email messages, Python might raise an exception that says imaplib.error: got more than 10000 bytes. When this happens, you will have to disconnect and reconnect to the IMAP server and try again.

This limit is in place to prevent your Python programs from eating up too much memory. The default size limit is often too small thus we can change this limit from 10,000 bytes to 10,000,000 bytes as shown below:

 import imaplib
 imaplib._MAXLINE = 10000000


Deleting Emails

To delete emails, pass a list of message UIDs to the IMAPClient object’s delete_messages() method. This marks the emails with the \Deleted flag. Calling the expunge() method will permanently delete all emails with the \Deleted flag in the currently selected folder. See the following program:

import imapclient

imapObj.select_folder('INBOX', readonly=True)

UIDs = imapObj.search(['SINCE 04-Jan-2019'])

imapObj.delete_messages(UIDs)

imapObj.expunge()

imapObj.logout()


Calling delete_message() and passing it UIDs returns a dictionary; each key-value pair is a message ID and a tuple of the message’s flags, which should now include \Deleted. Calling expunge() then permanently deletes messages with the \Deleted flag and returns a success message if there were no problems expunging the emails. Note that some email providers, such as Gmail, automatically expunge emails deleted with delete_messages() instead of waiting for an expunge command from the IMAP client.

When the program has finished retrieving or deleting emails, we call the IMAPClient’s logout() method to disconnect from the IMAP server.

Here I am ending today's topic. So till we meet next keep practicing and learning Python as Python is easy to learn!




Share:

0 comments:

Post a Comment