简体   繁体   中英

Download Attachments from gmail using Gmail API

I am using Gmail API to access my gmail data and google python api client.

According to documentation to get the message attachment they gave one sample for python

https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get

but the same code i tried then i am getting error:

AttributeError: 'Resource' object has no attribute 'user'

line where i am getting error:

message = service.user().messages().get(userId=user_id, id=msg_id).execute()

So i tried users() by replacing user()

message = service.users().messages().get(userId=user_id, id=msg_id).execute()

but i am not getting part['body']['data'] in for part in message['payload']['parts']

Expanding @Eric answer, I wrote the following corrected version of GetAttachments function from the docs:

# based on Python example from 
# https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get
# which is licensed under Apache 2.0 License

import base64
from apiclient import errors

def GetAttachments(service, user_id, msg_id):
    """Get and store attachment from Message with given id.

    :param service: Authorized Gmail API service instance.
    :param user_id: User's email address. The special value "me" can be used to indicate the authenticated user.
    :param msg_id: ID of Message containing attachment.
    """
    try:
        message = service.users().messages().get(userId=user_id, id=msg_id).execute()

        for part in message['payload']['parts']:
            if part['filename']:
                if 'data' in part['body']:
                    data = part['body']['data']
                else:
                    att_id = part['body']['attachmentId']
                    att = service.users().messages().attachments().get(userId=user_id, messageId=msg_id,id=att_id).execute()
                    data = att['data']
                file_data = base64.urlsafe_b64decode(data.encode('UTF-8'))
                path = part['filename']

                with open(path, 'w') as f:
                    f.write(file_data)

    except errors.HttpError, error:
        print 'An error occurred: %s' % error

You can still miss attachments by following @Ilya V. Schurov or @Cam T answers, the reason is because the email structure can be different based on the mimeType .

Inspired by this answer , here is my approach to the problem.

import base64
from apiclient import errors

def GetAttachments(service, user_id, msg_id, store_dir=""):
    """Get and store attachment from Message with given id.
        Args:
            service: Authorized Gmail API service instance.
            user_id: User's email address. The special value "me"
                can be used to indicate the authenticated user.
            msg_id: ID of Message containing attachment.
            store_dir: The directory used to store attachments.
    """
    try:
        message = service.users().messages().get(userId=user_id, id=msg_id).execute()
        parts = [message['payload']]
        while parts:
            part = parts.pop()
            if part.get('parts'):
                parts.extend(part['parts'])
            if part.get('filename'):
                if 'data' in part['body']:
                    file_data = base64.urlsafe_b64decode(part['body']['data'].encode('UTF-8'))
                    #self.stdout.write('FileData for %s, %s found! size: %s' % (message['id'], part['filename'], part['size']))
                elif 'attachmentId' in part['body']:
                    attachment = service.users().messages().attachments().get(
                        userId=user_id, messageId=message['id'], id=part['body']['attachmentId']
                    ).execute()
                    file_data = base64.urlsafe_b64decode(attachment['data'].encode('UTF-8'))
                    #self.stdout.write('FileData for %s, %s found! size: %s' % (message['id'], part['filename'], attachment['size']))
                else:
                    file_data = None
                if file_data:
                    #do some staff, e.g.
                    path = ''.join([store_dir, part['filename']])
                    with open(path, 'w') as f:
                        f.write(file_data)
    except errors.HttpError as error:
        print 'An error occurred: %s' % error

i tested codes above and doesn't worked. And i updated some stuff for other posts. WriteFileError

    import base64
    from apiclient import errors


    def GetAttachments(service, user_id, msg_id, prefix=""):
       """Get and store attachment from Message with given id.

       Args:
       service: Authorized Gmail API service instance.
       user_id: User's email address. The special value "me"
       can be used to indicate the authenticated user.
       msg_id: ID of Message containing attachment.
       prefix: prefix which is added to the attachment filename on saving
       """
       try:
           message = service.users().messages().get(userId=user_id, id=msg_id).execute()

           for part in message['payload'].get('parts', ''):
              if part['filename']:
                  if 'data' in part['body']:
                     data=part['body']['data']
                  else:
                     att_id=part['body']['attachmentId']
                     att=service.users().messages().attachments().get(userId=user_id, messageId=msg_id,id=att_id).execute()
                     data=att['data']
            file_data = base64.urlsafe_b64decode(data.encode('UTF-8'))
            path = prefix+part['filename']

            with open(path, 'wb') as f:
                f.write(file_data)

        except errors.HttpError as error:
            print('An error occurred: %s' % error)

It's definitely "users()". The format of the response Message is largely dependent on the format parameter you use. If you use the default (FULL) then parts will either have part['body']['data'] or, when data is large, with an "attachment_id" field that you can pass to messages().attachments().get().

If you look at the attachments docs you'll see this: https://developers.google.com/gmail/api/v1/reference/users/messages/attachments

(Would be nice if this was also mentioned on the main messages docs page also.)

I made the following changes for the code above and works totally fine for every email id contains attachment documents, I hope this can help because with the API example you will get an error Key.

def GetAttachments(service, user_id, msg_id, store_dir):

"""Get and store attachment from Message with given id.

Args:
service: Authorized Gmail API service instance.
user_id: User's email address. The special value "me"
can be used to indicate the authenticated user.
msg_id: ID of Message containing attachment.
prefix: prefix which is added to the attachment filename on saving
"""
try:
    message = service.users().messages().get(userId=user_id, id=msg_id).execute()
    for part in message['payload']['parts']:
        newvar = part['body']
        if 'attachmentId' in newvar:
            att_id = newvar['attachmentId']
            att = service.users().messages().attachments().get(userId=user_id, messageId=msg_id, id=att_id).execute()
            data = att['data']
            file_data = base64.urlsafe_b64decode(data.encode('UTF-8'))
            print(part['filename'])
            path = ''.join([store_dir, part['filename']])
            f = open(path, 'wb')
            f.write(file_data)
            f.close()
except errors.HttpError, error:
    print 'An error occurred: %s' % error

Google Official API for Attachments

Thanks to @Ilya V. Schurov and @Todor for the answers. You can still miss messages in the case when there are mails with and without attachment for the same search string. Here is my approach to getting mail body for both type of mails ie with attachment and without attachment.

def get_attachments(service, msg_id):
try:
    message = service.users().messages().get(userId='me', id=msg_id).execute()

    for part in message['payload']['parts']:
        if part['filename']:
            if 'data' in part['body']:
                data = part['body']['data']
            else:
                att_id = part['body']['attachmentId']
                att = service.users().messages().attachments().get(userId='me', messageId=msg_id,id=att_id).execute()
                data = att['data']
            file_data = base64.urlsafe_b64decode(data.encode('UTF-8'))
            path = part['filename']

            with open(path, 'wb') as f:
                f.write(file_data)
    

except errors.HttpError as error:
    print ('An error occurred: %s') % error

def get_message(service,msg_id):
try:
    message = service.users().messages().get(userId='me', id=msg_id).execute()
    if message['payload']['mimeType'] == 'multipart/mixed':
        for part in message['payload']['parts']:
            for sub_part in part['parts']:
                if sub_part['mimeType'] == 'text/plain':
                    data = sub_part['body']['data']
                    break
            if data:
                break           
    else:
        for part in message['payload']['parts']:
            if part['mimeType'] == 'text/plain':
                data = part['body']['data']
                break
    
    content = base64.b64decode(data).decode('utf-8')
    print(content)

    return content    

except errors.HttpError as error:
    print("An error occured : %s") %error
from __future__ import print_function
import base64
import os.path
import oauth2client
from googleapiclient.discovery import build
from oauth2client import file,tools
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
store_dir=os.getcwd()

def attachment_download():

store = oauth2client.file.Storage('credentials_gmail.json')
creds = store.get()
if not creds or creds.invalid:
    flow = oauth2client.client.flow_from_clientsecrets('client_secrets.json', SCOPES)
    creds = oauth2client.tools.run_flow(flow, store)


try:
    service = build('gmail', 'v1', credentials=creds)
    results = service.users().messages().list(userId='me', labelIds=['XXXX']).execute() # XXXX is label id use INBOX to download from inbox
    messages = results.get('messages', [])
    for message in messages:
        msg = service.users().messages().get(userId='me', id=message['id']).execute()
        for part in msg['payload'].get('parts', ''):

            if part['filename']:
                if 'data' in part['body']:
                    data = part['body']['data']
                else:
                    att_id = part['body']['attachmentId']
                    att = service.users().messages().attachments().get(userId='me', messageId=message['id'],id=att_id).execute()
                    data = att['data']
                file_data = base64.urlsafe_b64decode(data.encode('UTF-8'))

                filename = part['filename']
                print(filename)
                path = os.path.join(store_dir + '\\' 'Downloaded files' + '\\' + filename)

                with open(path, 'wb') as f:
                    f.write(file_data)
                    f.close()
except Exception as error:
    print(error)

Please See: To get label id https://developers.google.com/gmail/api/v1/reference/users/labels/list

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM