5

I am trying to read all the unread emails from the gmail account. The above code is able to make connection but is unable to fetch the emails.

I want to print the content of each email.

I am getting the error as can't concat int to bytes.

code:

import smtplib
import time
import imaplib
import email

def read_email_from_gmail():
        mail = imaplib.IMAP4_SSL('imap.gmail.com')
        mail.login('my_mail','my_pwd')
        mail.select('inbox')

        result, data = mail.search(None, 'ALL')
        mail_ids = data[0]

        id_list = mail_ids.split()   
        first_email_id = int(id_list[0])
        latest_email_id = int(id_list[-1])


        for i in range(latest_email_id,first_email_id, -1):
            result, data = mail.fetch(i, '(RFC822)' )

            for response_part in data:
                if isinstance(response_part, tuple):
                    msg = email.message_from_string(response_part[1])
                    email_subject = msg['subject']
                    email_from = msg['from']
                    print ('From : ' + email_from + '\n')
                    print ('Subject : ' + email_subject + '\n')



print(read_email_from_gmail())

error:

Traceback (most recent call last):
  File "C:/Users/devda/Desktop/Internship/access_email.py", line 32, in <module>
    print(read_email_from_gmail())
  File "C:/Users/devda/Desktop/Internship/access_email.py", line 20, in read_email_from_gmail
    result, data = mail.fetch(i, '(RFC822)' )
  File "C:\Users\devda\AppData\Local\Programs\Python\Python36\lib\imaplib.py", line 529, in fetch
    typ, dat = self._simple_command(name, message_set, message_parts)
  File "C:\Users\devda\AppData\Local\Programs\Python\Python36\lib\imaplib.py", line 1191, in _simple_command
    return self._command_complete(name, self._command(name, *args))
  File "C:\Users\devda\AppData\Local\Programs\Python\Python36\lib\imaplib.py", line 956, in _command
    data = data + b' ' + arg
TypeError: can't concat int to bytes
>>> 

I followed the tutorial from here

What I want to do is to extract content from email which is shown in image Email

Aniket Bote
  • 3,456
  • 3
  • 15
  • 33
  • I guess you need `str(i)` though I don't have the time to look into that properly now. – tripleee Dec 18 '18 at 06:39
  • As an aside, the function doesn't return anything useful that you can print; so the final print will simply be `None`. Perhaps a better design would be simply to have the function `return` the information you extracted, and have the caller decide if they want to `print()` it or do something else with it. – tripleee Dec 18 '18 at 06:41
  • I tried str(i) it gives me type error of initial value must be str or none ,not bytes. – Aniket Bote Dec 18 '18 at 06:44
  • Hmm, where did you change that? I mean `result, data = mail.fetch(str(i), '(RFC822)' )` – tripleee Dec 18 '18 at 06:45
  • Yes over there. Should I post the traceback? – Aniket Bote Dec 18 '18 at 06:45
  • try change the line msg = email.message_from_string(response_part[1]) to msg = email.message_from_bytes(response_part[1]) – Frank Hag Mar 17 '19 at 21:06

2 Answers2

6

I had to make a few changes to your code in order to get it to work on Python 3.5.1. I have inlined comments below.

# no need to import smtplib for this code
# no need to import time for this code
import imaplib
import email


def read_email_from_gmail():
        mail = imaplib.IMAP4_SSL('imap.gmail.com')
        mail.login('my_mail','my_pwd')
        mail.select('inbox')

        result, data = mail.search(None, 'ALL')
        mail_ids = data[0]

        id_list = mail_ids.split()   
        first_email_id = int(id_list[0])
        latest_email_id = int(id_list[-1])

        for i in range(latest_email_id,first_email_id, -1):
            # need str(i)
            result, data = mail.fetch(str(i), '(RFC822)' )

            for response_part in data:
                if isinstance(response_part, tuple):
                    # from_bytes, not from_string
                    msg = email.message_from_bytes(response_part[1])
                    email_subject = msg['subject']
                    email_from = msg['from']
                    print ('From : ' + email_from + '\n')
                    print ('Subject : ' + email_subject + '\n')

# nothing to print here
read_email_from_gmail()

Maybe submit a bug report to the author of that blog.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • I got the from part and subject part correct. How should i get the content too. – Aniket Bote Dec 18 '18 at 07:02
  • That's significantly beyond the scope of this question. Briefly, inspect the MIME structure and extract the parts you want. See maybe https://stackoverflow.com/questions/16411710/get-body-text-of-an-email-using-python-imap-and-email-package – tripleee Dec 18 '18 at 07:03
  • something was funky with the first `for` loop when reading a mailbox with only 1 email, and also I changed the filter to read only UNSEEN emails, and mark them as read: https://gist.github.com/philshem/4ee388f7dc045366c16eb9730ca630a9 – philshem Sep 01 '20 at 07:42
  • Why do you `enumerate` if you don't care about the enumeration? And why do you convert to `int` only to then immediately convert to `str`? – tripleee Sep 01 '20 at 07:50
4

This is what worked for me: It stores the from address, subject, content(text) into a file of all unread emails. code:

import email
import imaplib
mail = imaplib.IMAP4_SSL('imap.gmail.com')
(retcode, capabilities) = mail.login('mymail','mypassword')
mail.list()
mail.select('inbox')

n=0
(retcode, messages) = mail.search(None, '(UNSEEN)')
if retcode == 'OK':

   for num in messages[0].split() :
      print ('Processing ')
      n=n+1
      typ, data = mail.fetch(num,'(RFC822)')
      for response_part in data:
         if isinstance(response_part, tuple):
             original = email.message_from_bytes(response_part[1])

            # print (original['From'])
            # print (original['Subject'])
             raw_email = data[0][1]
             raw_email_string = raw_email.decode('utf-8')
             email_message = email.message_from_string(raw_email_string)
             for part in email_message.walk():
                        if (part.get_content_type() == "text/plain"): # ignore attachments/html
                              body = part.get_payload(decode=True)
                              save_string = str(r"C:\Users\devda\Desktop\Internship\Dumpemail_" + str('richboy') + ".txt" )
                              myfile = open(save_string, 'a')
                              myfile.write(original['From']+'\n')
                              myfile.write(original['Subject']+'\n')            
                              myfile.write(body.decode('utf-8'))
                              myfile.write('**********\n')
                              myfile.close()
                        else:
                              continue

             typ, data = mail.store(num,'+FLAGS','\\Seen')

print (n)
Aniket Bote
  • 3,456
  • 3
  • 15
  • 33