How can I download multiple attachments from a single mail using imaplib?
Let's say I have an e-mail and that e-mail contains 4 attachments. How can I download all of those attachments? The code below only downloads a single attachment from an e-mail.
detach_dir = 'c:/downloads'
m = imaplib.IMAP4_SSL("imap.gmail.com")
m.login('[email protected]','3323434')
m.select("[Gmail]/All Mail")
resp, items = m.search(None, "(UNSEEN)")
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)
temp = m.store(emailid,'+FLAGS', '\\Seen')
m.expunge()
if mail.get_content_maintype() != 'multipart':
continue
print "["+mail["From"]+"] :" + mail["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()
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()
return HttpResponse('check folder')
You need to select the messages from which you wish to download the attachments in bulk all at one place. Go to the tab Message and click on Attachments. Next, tap on Download all Attachments. You need to select the directory location to save all your attachments.
Download the attached file. Click on the thumbnail icon for the attached file. Then right-click and select the "Save" option. You also can download the attachment by right-clicking it and selecting "Download."
In general, you can try analyzing content-type header. If it's multipart/mixed, most likely the message contains an attachment.
For any future python travellers. Here is a class that downloads any attachment found for an email and saves it to a specific location.
import email import imaplib import os class FetchEmail(): connection = None error = None def __init__(self, mail_server, username, password): self.connection = imaplib.IMAP4_SSL(mail_server) self.connection.login(username, password) self.connection.select(readonly=False) # so we can mark mails as read def close_connection(self): """ Close the connection to the IMAP server """ self.connection.close() def save_attachment(self, msg, download_folder="/tmp"): """ Given a message, save its attachments to the specified download folder (default is /tmp) return: file path to attachment """ att_path = "No attachment found." for part in msg.walk(): if part.get_content_maintype() == 'multipart': continue if part.get('Content-Disposition') is None: continue filename = part.get_filename() att_path = os.path.join(download_folder, filename) if not os.path.isfile(att_path): fp = open(att_path, 'wb') fp.write(part.get_payload(decode=True)) fp.close() return att_path def fetch_unread_messages(self): """ Retrieve unread messages """ emails = [] (result, messages) = self.connection.search(None, 'UnSeen') if result == "OK": for message in messages[0].split(' '): try: ret, data = self.connection.fetch(message,'(RFC822)') except: print "No new emails to read." self.close_connection() exit() msg = email.message_from_bytes(data[0][1]) if isinstance(msg, str) == False: emails.append(msg) response, data = self.connection.store(message, '+FLAGS','\\Seen') return emails self.error = "Failed to retreive emails." return emails def parse_email_address(self, email_address): """ Helper function to parse out the email address from the message return: tuple (name, address). Eg. ('John Doe', '[email protected]') """ return email.utils.parseaddr(email_address)
I reworked the code, breaking it up into functions. I use PEEK
so I don't change the UNREAD status of the email messages.
I'm posting my take on the problem, similar to @John, but I use only functions instead of classes:
import imaplib import email # Connect to an IMAP server def connect(server, user, password): m = imaplib.IMAP4_SSL(server) m.login(user, password) m.select() return m # Download all attachment files for a given email def downloaAttachmentsInEmail(m, emailid, outputdir): resp, data = m.fetch(emailid, "(BODY.PEEK[])") email_body = data[0][1] mail = email.message_from_string(email_body) if mail.get_content_maintype() != 'multipart': return for part in mail.walk(): if part.get_content_maintype() != 'multipart' and part.get('Content-Disposition') is not None: open(outputdir + '/' + part.get_filename(), 'wb').write(part.get_payload(decode=True)) # Download all the attachment files for all emails in the inbox. def downloadAllAttachmentsInInbox(server, user, password, outputdir): m = connect(server, user, password) resp, items = m.search(None, "(ALL)") items = items[0].split() for emailid in items: downloaAttachmentsInEmail(m, emailid, outputdir)
You code appears okay except for the return
(perhaps a typo?) right after the fp.close()
:
...
fp.write(part.get_payload(decode=True))
fp.close()
return HttpResponse('check folder')
After saving the first attachment it returns from the function. Comment out that line and see if it fixes your issue.
You may use imap_tools package: https://pypi.org/project/imap-tools/
from imap_tools import MailBox
with MailBox('imap.mail.com').login('[email protected]', 'password', 'INBOX') as mailbox:
for message in mailbox.fetch():
for att in message.attachments: # list: [Attachment objects]
att.filename # str: 'cat.jpg'
att.content_type # str: 'image/jpeg'
att.payload # bytes: b'\xff\xd8\xff\xe0\'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With