I want to get unread emails from my inbox with python code.
I set up google developer account, I made an app (I set it to DESKTOP) and I downloaded credentials.
{"installed":{"client_id":"xxx",
"project_id":"xxx",
"auth_uri":"https://accounts.google.com/o/oauth2/auth",
"token_uri":"https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs",
"client_secret":"xxx",
"redirect_uris":["http://localhost"]
}
}
This is the code that I have:
import os
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
creds = Credentials.from_authorized_user_file(os.path.expanduser('gmail_credencials.json'), ['https://www.googleapis.com/auth/gmail.readonly'])
service = build('gmail', 'v1', credentials=creds)
print(service)
messages = service.users().messages()
print(messages)
But I am getting this error:
ValueError: Authorized user info was not in the expected format, missing fields refresh_token, client_secret, client_id.
I have client_secret and client_id, but I do not have a clue where should I get refresh_token.
Does anyone else has the experience with this error?
This is a sample code based on the Google Documentation here. It has an example about the refresh token on it. I'm also listing the files emails that have the label UNREAD. After that, I get the emails with the IDs of the list, and decode the messages payload using base64. Take in consideration that this is just a sample.
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
import email
import base64 #add Base64
import time
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
#Filter and get the IDs of the message I need.
#I'm just filtering messages that have the label "UNREAD"
try:
service = build('gmail', 'v1', credentials=creds)
search_id = service.users().messages().list(userId='me', labelIds="UNREAD").execute()
number_result = search_id['resultSizeEstimate']
final_list = [] # empty array, all the messages ID will be listed here
# review if the search is empty or not
# if it has messages on it, It will enter the for
if number_result>0:
message_ids = search_id['messages']
for ids in message_ids:
final_list.append(ids['id'])
# call the function that will call the body of the message
get_message(service, ids['id'] )
return final_list
# If there are not messages with those criterias
#The message 'There were 0 results for that search string' will be printed.
else:
print('There were 0 results for that search string')
return ""
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
#new function to get the body of the message, and decode the message
def get_message(service, msg_id):
try:
message_list=service.users().messages().get(userId='me', id=msg_id, format='raw').execute()
msg_raw = base64.urlsafe_b64decode(message_list['raw'].encode('ASCII'))
msg_str = email.message_from_bytes(msg_raw)
content_types = msg_str.get_content_maintype()
#how it will work when is a multipart or plain text
if content_types == 'multipart':
part1, part2 = msg_str.get_payload()
print("This is the message body, html:")
print(part1.get_payload())
return part1.get_payload()
else:
print("This is the message body plain text:")
print(msg_str.get_payload())
return msg_str.get_payload()
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
Reference:
Method: users.messages.list
Method: users.messages.get
Format
Google Documentation "Python quickstart"
base64 Python
Related
So my colleague and I have an application whereby we need to capture the OAuth Redirect from Google's OAuth Server Response, the reason being is we need to send a payload to capture to renew our pickle token, and we need to do it without human intervention. The code looks like this:
#!/usr/bin/env python3
import pickle
import os.path
import pandas as pd
import requests
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
import base64
from datetime import datetime, timedelta
from urllib.parse import unquote
from bs4 import BeautifulSoup
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def search_message(service, user_id, search_string):
"""
Search the inbox for emails using standard gmail search parameters
and return a list of email IDs for each result
PARAMS:
service: the google api service object already instantiated
user_id: user id for google api service ('me' works here if
already authenticated)
search_string: search operators you can use with Gmail
(see https://support.google.com/mail/answer/7190?hl=en for a list)
RETURNS:
List containing email IDs of search query
"""
try:
# initiate the list for returning
list_ids = []
# get the id of all messages that are in the search string
search_ids = service.users().messages().list(userId=user_id, q=search_string).execute()
# if there were no results, print warning and return empty string
try:
ids = search_ids['messages']
except KeyError:
print("WARNING: the search queried returned 0 results")
print("returning an empty string")
return ""
if len(ids) > 1:
for msg_id in ids:
list_ids.append(msg_id['id'])
return (list_ids)
else:
list_ids.append(ids['id'])
return list_ids
except:
print("An error occured: %s")
def get_message(service, user_id, msg_id):
"""
Search the inbox for specific message by ID and return it back as a
clean string. String may contain Python escape characters for newline
and return line.
PARAMS
service: the google api service object already instantiated
user_id: user id for google api service ('me' works here if
already authenticated)
msg_id: the unique id of the email you need
RETURNS
A string of encoded text containing the message body
"""
try:
final_list = []
message = service.users().messages().get(userId=user_id, id=msg_id).execute() # fetch the message using API
payld = message['payload'] # get payload of the message
report_link = ""
mssg_parts = payld['parts'] # fetching the message parts
part_one = mssg_parts[1] # fetching first element of the part
#part_onee = part_one['parts'][1]
#print(part_one)
part_body = part_one['body'] # fetching body of the message
part_data = part_body['data'] # fetching data from the body
clean_one = part_data.replace("-", "+") # decoding from Base64 to UTF-8
clean_one = clean_one.replace("_", "/") # decoding from Base64 to UTF-8
clean_one = clean_one.replace("amp;", "") # cleaned amp; in links
clean_two = base64.b64decode(clean_one) # decoding from Base64 to UTF-8
soup = BeautifulSoup(clean_two, features="html.parser")
for link in soup.findAll('a'):
if "talentReportRedirect?export" in link.get('href'):
report_link = link.get('href')
break
final_list.append(report_link) # This will create a dictonary item in the final list
except Exception:
print("An error occured: %s")
return final_list
def get_service():
"""
Authenticate the google api client and return the service object
to make further calls
PARAMS
None
RETURNS
service api object from gmail for making calls
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
auth_link = build('gmail', 'v1', credentials=creds)
parsed_url = unquote(auth_link).split('redirect')[-1]
return parsed_url
def get_report(link_array):
for link in link_array:
df = requests.get(link[0], allow_redirects=True)
# df.encoding
# dt = pd.DataFrame(data=df)
print(link)
# upload_to_database(df) -- Richard Barret please update this function
print(df.text)
## dt.to_csv(r'C:\Users\user\Desktop\api_gmail.csv', sep='\t',header=True)
if __name__ == "__main__":
link_list = []
monday = datetime(2022,12,5)#datetime.now() - timedelta(days=datetime.now().weekday())
thursday = datetime(2022,12,8)#datetime.now() - timedelta(days=datetime.now().weekday() - 3)
query = 'from:messages-noreply#linkedin.com ' + 'after:' + monday.strftime('%Y/%m/%d') + ' before:' + thursday.strftime('%Y/%m/%d')
service = get_service()
mssg_list = search_message(service, user_id='me', search_string=query)
for msg in mssg_list:
link_list.append(get_message(service, user_id='me', msg_id=msg))
get_report(link_list)
It is assumed that you have a directory structure like this:
├── credentials.json
├── gmail_api_linkedin.py
└── requirements.txt
Obviously, you won't have the credentials.json file, but in essence, the code works and redirects us to a login page to retrieve the new pickle:
The main thing is we can't interact with that in an autonomous fashion. As such, how can we capture the URL from the server that prints out the following information the is differenter every single time.
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=212663976989-96o952s9ujadjgfdp6fm0p462p37opml.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A58605%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly&state=ztJir0haFQlvTP79BRthhmEHlSsqIj&access_type=offline
More succinctly, how can we capture the URL in a pythonic manner to send POST and PUT requests to that redirect?
renew our pickle token
I still do not understand why you feel the need to renew your token pickle.
how it all works.
The following example will spawn the consent screen directly on the machine its running on. It then stores the token within the token.json file
token.json
This file contains all the information needed by the script to run. It can automatically request a new access token when ever it needs.
{
"token": "[REDACTED]",
"refresh_token": "[REDACTED]",
"token_uri": "https://oauth2.googleapis.com/token",
"client_id": "[REDACTED]",
"client_secret": "[REDACTED],
"scopes": [
"https://mail.google.com/"
],
"expiry": "2023-01-03T19:06:13.959468Z"
}
gmail quickstart.
# To install the Google client library for Python, run the following command:
# pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
from __future__ import print_function
import os.path
import google.auth.exceptions
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://mail.google.com/']
def main():
"""Shows basic usage of the Gmail v1 API.
Prints a list of user messages.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
try:
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
creds.refresh(Request())
except google.auth.exceptions.RefreshError as error:
# if refresh token fails, reset creds to none.
creds = None
print(f'An error occurred: {error}')
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'C:\YouTube\dev\credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('gmail', 'v1', credentials=creds)
# Call the Gmail v1 API
results = service.users().messages().list(
userId='me').execute()
messages = results.get('messages', [])
if not messages:
print('No messages found.')
return
print('Messages:')
for message in messages:
print(u'{0} ({1})'.format(message['id'], message['threadId']))
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
expired refresh token.
If your issue is in fact that your refresh tokens are expiring this is because your app is currently in the testing phase. If you set your app to production then your refresh tokens will stope expiring.
I keep getting this error and have looked everywhere and I cannot fix it. Can someone please hold my hand. I have downloaded the json filed and named it so it matches. I have watched every youtube video to make sure I created credentials correctly and also added my email as a test user. Not sure what to do at this point. I have json file and the token in my IDE, also I was able to log in and choose my username and got the flow message so everything seems to be working. Up until I copy and paste the create_email_draft from gmail api documents. Basically I just want to send some automated emails. Here is my code...let me know if you need anything else.
ERROR:
Traceback (most recent call last):
File "C:\Users\carlo\PycharmProjects\pythonProject3\quickstart.py", line 61, in
gmail_create_draft()
File "C:\Users\carlo\PycharmProjects\pythonProject3\quickstart.py", line 25, in gmail_create_draft
creds, _ = google.auth.default()
File "C:\Users\carlo\PycharmProjects\pythonProject3\venv\lib\site-packages\google\auth_default.py", line 616, in default
raise exceptions.DefaultCredentialsError(_HELP_MESSAGE)
google.auth.exceptions.DefaultCredentialsError: Could not automatically determine credentials. Please set GOOGLE_APPLICATION_CREDENTIALS or explicitly create credentials and re-run the application. For more information, please see https://cloud.google.com/docs/authentication/getting-started
Process finished with exit code 1
from __future__ import print_function
import os.path
import base64
from email.message import EmailMessage
import google.auth
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def gmail_create_draft():
"""Create and insert a draft email.
Print the returned draft's message and id.
Returns: Draft object, including draft id and message meta data.
Load pre-authorized user credentials from the environment.
TODO(developer) - See https://developers.google.com/identity
for guides on implementing OAuth2 for the application.
"""
creds, _ = google.auth.default()
try:
# create gmail api client
service = build('gmail', 'v1', credentials=creds)
message = EmailMessage()
message.set_content('This is automated draft mail')
message['To'] = 'carlosmedina239#gmail.com'
message['From'] = 'selenytesting123#gmail.com'
message['Subject'] = 'Automated draft'
# encoded message
encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
create_message = {
'message': {
'raw': encoded_message
}
}
# pylint: disable=E1101
draft = service.users().drafts().create(userId="me",
body=create_message).execute()
print(F'Draft id: {draft["id"]}\nDraft message: {draft["message"]}')
except HttpError as error:
print(F'An error occurred: {error}')
draft = None
return draft
if __name__ == '__main__':
gmail_create_draft()
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
# You don't have to care about anything on top of this comment
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
if not labels:
print('No labels found.')
return
print('Labels:')
for label in labels:
print(label['name'])
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
I think you have tried to copy the code from the quickstart without trying understanding what its doing.
Your gmail_create_draft method is not building the credentials properly. So its looking for it in the default env var which you dont have set which is the cause of your error message.
I have tested this code and it works.
# To install the Google client library for Python, run the following command:
# pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
from __future__ import print_function
import os.path
import base64
from email.message import EmailMessage
import google.auth
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://mail.google.com/']
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('tokenSend.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('tokenSend.json', 'w') as token:
token.write(creds.to_json())
# You don't have to care about anything on top of this comment
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
message = EmailMessage()
message.set_content('This is automated draft mail')
message['To'] = 'xxxx#gmail.com'
message['From'] = 'xxxx#gmail.com'
message['Subject'] = 'Automated draft'
# encoded message
encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
create_message = {
'message': {
'raw': encoded_message
}
}
# pylint: disable=E1101
draft = service.users().drafts().create(userId="me",
body=create_message).execute()
print(F'Draft id: {draft["id"]}\nDraft message: {draft["message"]}')
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
Results in
Draft id: r4575751609218968324
Draft message: {'id': '182fcd72a7a06a96', 'threadId': '182fcd72a7a06a96', 'labelIds': ['DRAFT']}
I am trying to download the attachment from gmail using the python and I am not able to fetch the attachment id from my mail. Please find my code below
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
def get_gmail_service():
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
return service
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
def get_email_list():
service = get_gmail_service()
results = service.users().messages().list(userId='me',q='from:abc#gmail.com is:read').execute()
# print(results.get('messages',[])[0].get('id',[]))
return results.get('messages', [])[0].get('id', [])
# return results.get('messages',[])
def get_email_content(message_id):
service = get_gmail_service()
attach = service.users().messages().get(userId='me',id =message_id).execute()
attach_id = attach.get('payloads',[]).get('parts',[]).get('body',[])
data = service.users().messages().get(userId='me',id = message_id).execute()
return attach_id
if __name__ == '__main__':
# get_email_list()
print(get_email_content(get_email_list()))
Please correct my code so that I can download the attachment using the gmail api.
There are two main issues with this code.
results.get() method either returns a Message or MessagePart Object. So you only need to use the get() method once to get the complete object and then you can target the specific part of the object you want.
For Example. results.get('messages', [])[0]['id']
A payload for an email can be multipart (which means that "parts" will be an array of MessagePart objects). So we need to iterate over to get a "message part" that has a file. In this case, we can check if the MessagePart object has a filename.
parts = attach.get('payload',[])['parts']
for i in parts:
if( i['filename'] ):
return i['body']['attachmentId']
So After taking care of these two issues, this is the new code:
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
def get_gmail_service():
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
# Call the Gmail API
service = build('gmail', 'v1', credentials=creds)
return service
except HttpError as error:
# TODO(developer) - Handle errors from gmail API.
print(f'An error occurred: {error}')
def get_email_list():
service = get_gmail_service()
results = service.users().messages().list(userId='me',q='from:abc#gmail.com is:read').execute()
# print(results.get('messages',[])[0]['id'] )
return results.get('messages', [])[0]['id']
# return results.get('messages',[])
def get_email_content(message_id):
print(message_id)
service = get_gmail_service()
data = service.users().messages().get(userId='me',id = message_id).execute()
attach = service.users().messages().get(userId='me',id =message_id).execute()
parts = attach.get('payload',[])['parts']
for i in parts:
if( i['filename'] ):
return i['body']['attachmentId']
if __name__ == '__main__':
# get_email_list()
print(get_email_content(get_email_list()))
I hope this answers your question!
This is my code to download the pdf attachments using GMAIL API in Python. It works fine in sense that I am able to download the pdf attachments but they don't open. I get the following error "There was an error opening this document. The file is damaged and could not be repaired".
from __future__ import print_function
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
import base64
from apiclient import errors
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://mail.google.com/']
def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
# to manage a different gmail account, delete the existing token.json file from the folder
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
service = build('gmail', 'v1', credentials=creds)
# Call the Gmail API
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
# Call the Gmail API to fetch INBOX
results = service.users().messages().list(userId='me', labelIds=['Label_9213971794059785832']).execute()
messages = results.get('messages', [])
print(messages[1])
if not messages:
print("No messages found.")
else:
print("Message snippets:")
for message in messages:
GetAttachments(service,'me',message['id'])
# msg = service.users().messages().get(userId='me', id=message['id']).execute()
# print(msg)
# break
def GetAttachments(service, user_id, msg_id, store_dir="attachments/"):
"""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(str(file_data))
except errors.HttpError as error:
print('An error occurred: %s' % error)
if __name__ == '__main__':
main()
please help!
Found the mistake! I needed to write the bytes, not string.
modified two lines of the code as following:
with open(path, 'wb') as f:
f.write(file_data)
I would like to list all message ID's from a Gmail account utilizing the Gmail API. So far I've been able to list the first and second page of message ID's. I know I have to use the pageToken to get to the next page of results, but I can't figure out how to restructure my code so I'm not using 1,2,3, etc variables to call each page. Source code is below.
get_email_ids.py:
from __future__ import print_function
import os.path
from collections import Counter
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def main():
"""Shows basic usage of the Gmail API.
"""
creds = None
user_id = "me"
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
service = build('gmail', 'v1', credentials=creds)
### Call the Gmail API
### Show messages
token = ''
messages = service.users().messages().list(userId=user_id,pageToken=token).execute().get('messages', [])
token = service.users().messages().list(userId=user_id,pageToken=token).execute().get('nextPageToken', [])
print(messages,token)
messages2 = service.users().messages().list(userId=user_id,pageToken=token).execute().get('messages', [])
token2 = service.users().messages().list(userId=user_id,pageToken=token).execute().get('nextPageToken', [])
print(messages2,token2)
if __name__ == '__main__':
main()
Results of get_email_ids.py (shortened):
[{'id': '179ed5ae720de1f6', 'threadId': '179ed5ae720de1f6'}, ... {'id': '179ba226644a079a', 'threadId': '17972318184138fa'}] 09573475999783117733
[{'id': '179b9f8852d3b09d', 'threadId': '179b9f8852d3b09d'}, ... {'id': '1797fa390caa3454', 'threadId': '1797fa390caa3454'}] 07601624978802434502
I can't test it but I would use the same variables messages, token without 1,2,3 and results I would add to the same list with all messages. And I would run it in some loop.
Something like this
all_messages = []
token = ''
while True:
messages = service.users().messages().list(userId=user_id, pageToken=token).execute().get('messages', [])
token = service.users().messages().list(userId=user_id, pageToken=token).execute().get('nextPageToken', [])
print(messages, token)
if not messages:
break
#all_messages.extend(messages) # `extend` or `+=`, not `append`
all_messages += messages # `extend` or `+=`, not `append`
I only don't know how API informs that there is no more messages - maybe it returns empty list or maybe it gives empty token, or maybe it raise error.
EDIT:
Information for other users: as #emmalynnh mentioned in comment
When there are no more messages it gives an empty token
and the API will return a 400 error if you try to request.
A Improved version on #furas can be made.
all_messages = []
token = ''
while True:
service_messages = service.users().messages()
messages = service_messages.list(userId=user_id, pageToken=token).execute().get('messages', [])
token = service_messages.list(userId=user_id, pageToken=token).execute().get('nextPageToken', [])
if not messages:
break
all_messages += messages
print(all_messages)