0

I'm using the following code to download a Gsheet into Python. It works for some sheets but not all.

import pickle
import os.path
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build

def gsheet_api_check(SCOPES):
    creds = None
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    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)
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)
    return creds


def pull_sheet_data(SCOPES,SPREADSHEET_ID,RANGE_NAME):
    creds = gsheet_api_check(SCOPES)
    service = build('sheets', 'v4', credentials=creds)
    sheet = service.spreadsheets()
    #result = copy_file(service, SPREADSHEET_ID, copy_title)
    result = sheet.values().get(spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME).execute()
    values = result.get('values', [])

    if not values:
        print('No data found.')
    else:
        rows = sheet.values().get(spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME).execute()
        data = rows.get('values')
        print("COMPLETE: Data copied")
        return data

On the sheets that cause an error I recieve the following msg:

HttpError: <HttpError 400 when requesting https://sheets.googleapis.com/v4/spreadsheets/1bI_X2ssOj9gTYnKsa_CDajYP0FcFMr0E/values/3-7-14days?alt=json returned "This operation is not supported for this document">

I believe the error could be because the original spreadsheet was copied from a .xlsx format. I'm having trouble implementing a copy function to get around this error. Thanks in advance.

UPDATE

I changed the functions so that it incorporate both Drive API and Sheets API

    def gsheet_api_check(SCOPES):
    creds = None
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    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)
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)
    return creds

def drive_api_check(SCOPES):
    creds = None
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    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('drivecredentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)
    return creds

def pull_sheet_data(Sheets_SCOPES, Drive_SCOPES,SPREADSHEET_ID,RANGE_NAME):
    sheet_creds = gsheet_api_check(Sheets_SCOPES)
    drive_creds = drive_api_check(Drive_SCOPES)
    sheet_service = build('sheets', 'v4', credentials=sheet_creds)
    drive_service = build('drive', 'v3', credentials=drive_creds)
    drive_service.files().copy(fileId=SPREADSHEET_ID, body={"mimeType":"application/vnd.google-apps.spreadsheet"}).execute()
    #service.files().copy(fileId=SPREADSHEET_ID,convert=true, body={"title": "specifyName"}).execute()
    sheet = sheet_service.spreadsheets()
    copy_title = 'temp'
    #result = copy_file(service, SPREADSHEET_ID, copy_title)
    result = sheet.values().get(spreadsheetId=SPREADSHEET_ID, 
   range=RANGE_NAME).execute()
    values = result.get('values', [])

    if not values:
        print('No data found.')
    else:
        rows = sheet.values().get(spreadsheetId=SPREADSHEET_ID, 
   range=RANGE_NAME).execute()
        data = rows.get('values')
        print("COMPLETE: Data copied")
        return data

Now I get the following error:

<HttpError 403 when requesting https://www.googleapis.com/drive/v3/files/1bI_X2ssOj9gTYnKsa_CDajYP0FcFMr0E/copy?alt=json returned "Insufficient Permission: Request had insufficient authentication scopes.">
4
  • Do you mean that the document that you request with SPREADSHEET_ID is a .xlsx file? Or is it a Google Sheets file to which an .xlsx file has been converted? How did you perform the conversion? Commented Nov 11, 2020 at 9:17
  • it was an excel which Google sheets converted. For some reason some of the files work file but some give that error Commented Nov 11, 2020 at 10:03
  • How did you convert the files? Commented Nov 11, 2020 at 10:06
  • It was sent to my gmail and I opened it automatically in gSheets Commented Nov 11, 2020 at 10:36

1 Answer 1

3

Before using the Sheets API on an Excel file, you need to convert it to a Google Spreadsheet

Opening an Excel file with Google sheets is not equivalent to converting it.

You can convert the file programamtically, e.g. with the Files: copy method of the Drive API.

Sample request:

service = build('drive', 'v3', credentials=creds)
service.files().copy(fileId=ID_OF_THE_EXCEL_FILE, body={"mimeType"="application/vnd.google-apps.spreadsheet"}).execute()

Use the id of the converted file as SPREADSHEET_ID for your request.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks very much but will this work with v4 of Gsheets API. v3 is being depreciated
Please do not confuse Drive API with Sheets API. Sheets API v3 is indeed deprecated, but as for Drive API - v3 is the newest version. You can even do the conversion with Drive API v2 (not deprecated neither) as done here. Note that if you called you sheet service service, you should give the drive service another name, e.g. driveService = build('drive', 'v3', credentials=creds)
Thanks, I seem to have run into more trouble after adding both the Drive and Sheets API. See my update above
You need to enable the scope for the Drive API in your GCP console, as well as assign both scopes to the variableSCOPES. Afer this, you need to delete your token file to trigger new authorization flow.
i get an error: Cell In[117], line 22 service.files().copy(fileId=SAMPLE_SPREADSHEET_ID, body={"mimeType"="application/vnd.google-apps.spreadsheet"}).execute() ^ SyntaxError: cannot assign to literal here. Maybe you meant '==' instead of '='? edit: i think i fixed it ... but now getting an error : AttributeError: 'Resource' object has no attribute 'files'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.