Skip to content

Instantly share code, notes, and snippets.

@styrmis
Created March 19, 2014 08:55
Show Gist options
  • Save styrmis/9637945 to your computer and use it in GitHub Desktop.
Save styrmis/9637945 to your computer and use it in GitHub Desktop.
Download all attachments from Gmail from a specific sender
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import email, getpass, imaplib, os, sys
# The following script will download all attachments from a given sender
# that you specify. Little to no error handling, use at your own risk!
#
# IMPORTANT: If you first used Gmail in the UK then you need to select
# a different mailbox (please see below and uncomment the appropriate line).
#
# Adapted from http://stackoverflow.com/questions/10182499/how-do-i-download-only-unread-attachments-from-a-specific-gmail-label
# Directory where to save attachments ('.' = current directory)
detach_dir = '.'
user = raw_input("Gmail username: ")
pwd = getpass.getpass("Password: ")
sender_email = raw_input("Enter the sender's email (to filter by): ")
# Connecting to the gmail imap server
m = imaplib.IMAP4_SSL("imap.gmail.com")
m.login(user,pwd)
# Select the mailbox
m.select("[Gmail]/All Mail")
# If you get an error "command SEARCH illegal in state AUTH, only allowed in states SELECTED"
# then it is likely that the mailbox you have tried to select doesn't exist.
# If you were originally a Google Mail user (i.e. a user in the UK) then you should uncomment
# the line below.
# m.select("[Google Mail]/All Mail") # here you a can choose a mail box like INBOX instead
# Alternatively you can search for emails in the inbox only
# m.select("INBOX")
# Use m.list() to get all the mailboxes
resp, items = m.search(None, 'FROM', '"%s"' % sender_email)
items = items[0].split()
for emailid in items:
resp, data = m.fetch(emailid, "(RFC822)")
email_body = data[0][1]
mail = email.message_from_string(email_body)
if mail.get_content_maintype() != 'multipart':
continue
subject = ""
if mail["subject"] is not None:
subject = mail["subject"]
print "["+mail["From"]+"] :" + subject
for part in mail.walk():
if part.get_content_maintype() == 'multipart':
continue
if part.get('Content-Disposition') is None:
continue
filename = part.get_filename()
counter = 1
if not filename:
filename = 'part-%03d%s' % (counter, 'bin')
counter += 1
att_path = os.path.join(detach_dir, filename)
if not os.path.isfile(att_path) :
fp = open(att_path, 'wb')
fp.write(part.get_payload(decode=True))
fp.close()
@RahulSapparapu
Copy link

I have tried executing this code but it shows the following error:

Traceback (most recent call last):
File "config.py", line 25, in
mail = email.message_from_string(email_body)
File "C:\Users\sappa\AppData\Local\Programs\Python\Python37-32\lib\email_init_.py", line 38, in message_from_string
return Parser(*args, **kws).parsestr(s)
File "C:\Users\sappa\AppData\Local\Programs\Python\Python37-32\lib\email\parser.py", line 68, in parsestr
return self.parse(StringIO(text), headersonly=headersonly)
TypeError: initial_value must be str or None, not bytes

@RahulSapparapu
Copy link

RahulSapparapu commented Jun 10, 2019

SOLVED IT 💯

                                                            mail = email.message_from_string(email_body)

The above line in the code must be replaced with the following line and it works perfectly

                                                            mail = email.message_from_bytes(email_body)

in short: "email.message_from_string" must be replaced with "email.message_from_bytes". Because the data is present in bytes not in the string format.

@anilmacharla25
Copy link

mycode: wokring fine

import imaplib,email,smtplib
import os

detach_dir = '.'
if 'attachments' not in os.listdir(detach_dir):
os.mkdir("attachments")

user='[email protected]'
pwd="delu@2250012"

imap_url='imap.gmail.com'

con= imaplib.IMAP4_SSL('imap.gmail.com')
con.login(user, pwd)
con.select('INBOX')

type, data=con.uid('search', None, '(HEADER Subject "invoice")')

typ, data = con.search(None, '(SUBJECT "invoice")')

type, data = con.search(None, 'ALL')

mail_ids = data[0]

id_list = mail_ids.split()

for msgId in data[0].split():
typ, messageParts = con.fetch(msgId, '(RFC822)')
if typ != 'OK':
print ('Error fetching mail.')
raise

emailBody = messageParts[0][1]
# print(emailBody)

mail = email.message_from_bytes(emailBody)
for part in mail.walk():
        if part.get_content_maintype() == 'multipart':
            # print(part.as_string())
            continue
        if part.get('Content-Disposition') is None:
            # print (part.as_string())
            # print("done")
            continue
        fileName = part.get_filename()
        print('done')
        if bool(fileName):
            filePath = os.path.join(detach_dir, 'attachments', fileName)
            if not os.path.isfile(filePath) :
                print (fileName)
                fp = open(filePath, 'wb')
                fp.write(part.get_payload(decode=True))
                fp.close()

@ntyagi-dev
Copy link

ntyagi-dev commented Apr 19, 2022

SOLVED IT 💯

                                                            mail = email.message_from_string(email_body)

The above line in the code must be replaced with the following line and it works perfectly

                                                            mail = email.message_from_bytes(email_body)

in short: "email.message_from_string" must be replaced with "email.message_from_bytes". Because the data is present in bytes not in the string format.

Thanks for that @RahulSapparapu !
In addition to that, I had to make the following minor changes to make it python3 compatible

  1. Use input in place of raw_input
  2. Use additional single quote at

('"[Gmail]/All Mail"')

  1. Use parentheses at

print("["+mail["From"]+"] :" + subject)

Finally, added the following to filename as my attachments were of the same name and were getting overwritten:

datestring = mail['date']
datestring2 = utils.parsedate_to_datetime(datestring).strftime("%Y_%m_%d")
filename = datestring2 + '_' + part.get_filename()

Thank you @styrmis for sharing this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment