如果我想在本地将 Google sheet 下载到 CSV,是否需要为 Google API v4 定义 "redirect_uris"?

If I want to download a Google sheet to CSV locally, do I need "redirect_uris" defined for Google API v4?

我正在尝试使用 Python API 和 Google API v4 在本地下载 Google sheet 并转换它到 CSV 文件。我很高兴这个 运行 作为我本地机器上的脚本。我不需要网络能力。我有这个 Python 代码 ...

from gsheets import Sheets
...
        sheets = Sheets.from_files('/Users/davea/Documents/workspace/chicommons/maps/web/configuration_g_client.json')
        url = 'https://docs.google.com/spreadsheets/d/1ifpqxxxxxxxxxxxxx5gmvevJ7jc-xxxxxxxxjDS8Me7U/edit#gid=0'
        s = sheets.get(url)
        out = s.sheets[3].to_csv('Spam.csv', encoding='utf-8', dialect='excel')
        print(out)

从 Google 下载的 configuration_g_client.json 文件如下所示 ...

{"web":
    {
        "client_id":"124xxxx.apps.googleusercontent.com",
        "project_id":"ccc-directory-xxxxxxxxx08","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":"xxxxxx13s9",
        "javascript_origins":["http://localhost:8000"]
    }
}

但是这一行

sheets = Sheets.from_files('/Users/davea/Documents/workspace/chicommons/maps/web/configuration_g_client.json')

死于

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/gsheets/api.py", line 31, in from_files
    creds = oauth2.get_credentials(scopes, secrets, storage, no_webserver)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/gsheets/oauth2.py", line 48, in get_credentials
    flow = client.flow_from_clientsecrets(secrets, scopes)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/oauth2client/_helpers.py", line 133, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/oauth2client/client.py", line 2134, in flow_from_clientsecrets
    client_type, client_info = clientsecrets.loadfile(filename,
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/oauth2client/clientsecrets.py", line 165, in loadfile
    return _loadfile(filename)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/oauth2client/clientsecrets.py", line 126, in _loadfile
    return _validate_clientsecrets(obj)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/oauth2client/clientsecrets.py", line 99, in _validate_clientsecrets
    raise InvalidClientSecretsError(
oauth2client.clientsecrets.InvalidClientSecretsError: Missing property "redirect_uris" in a client type of "web".

如果我想在本地 运行,我应该使用不同的客户端类型吗?否则,我需要为“redirect_uris”设置什么值?

编辑: 作为对给出答案的回应,我更新了我的凭据 JSON 文件以包含

"redirect_uris":["http://localhost:8000"]

现在当我在本地 运行 我的脚本时,它会尝试生成浏览器

这很糟糕,因为我想通过 cron 作业在本地 运行 脚本并且不想与浏览器交互。

编辑: 根据有关 运行 使用服务帐户设置脚本的建议。我按照此处的说明创建了服务帐户 - https://developers.google.com/ad-manager/api/authentication#service。以下是生成的项目和关键屏幕...

凭据 JSON 文件如下所示

{
  "type": "service_account",
  "project_id": "chicommons",
  "private_key_id": "ecf14d...",
  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCcSISusTKvBcO1\nRn9bdD/vYWYyoi0ERi5P0u8+iJQ6WUmYIkKG2aYfyp3kt1/KQJ4YW0M4/AUGAQlz\n5FdS1kTqHkmzwtd+FN8FE6...xxx.../OmXZkvjQej2eA6Txcm/hfuyebkrXnyUm\nycuVzNRsywvGQf/7gWP0y9oQ\n-----END PRIVATE KEY-----\n",
  "client_email": "google-sheets-download@chicommons.iam.gserviceaccount.com",
  "client_id": "116134130252723375625",
  "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_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/google-sheets-download%40chicommons.iam.gserviceaccount.com"
}

但是现在当我 运行 我的脚本试图下载我的 Google sheet 时,我得到这个错误

oauth2client.clientsecrets.InvalidClientSecretsError: Invalid file format. See https://developers.google.com/api-client-library/python/guide/aaa_client_secrets Expected a JSON object with a single property for a "web" or "installed" application

你的情况,下面的修改怎么样?

从您的 configuration_g_client.json 中,我注意到 "javascript_origins":["http://localhost:8000"] 而没有 redirect_uris。所以请将http://localhost:8080/添加到redirect_uris并再次测试。下图是将http://localhost:8080/设置为redirect_uris。在这种情况下,您可以按如下方式删除 javascript_origins

  • 在这里,请将http://localhost:8080/设置为redirect_uris的值,然后点击“保存”按钮。 http://localhost:8080/ 的最后一个字符是 /。似乎默认的 redirect_urishttp://localhost:8080/ for oauth2.get_credentials。请注意。

更新问题的答案:

  • 在您更新的问题中,您想使用服务帐户而不是使用 python 的 OAuth2 从 Google 驱动器下载 CSV 数据。

为此,我认为您的授权脚本需要修改。在这个回答中,我想提出一个示例脚本来使用服务帐户实现您的目标。

重要提示:

在使用服务账号的时候,有一点很重要。这是服务帐户的 Google 驱动器与您的 Google 驱动器不同。因此,为了在您的 Google 驱动器中下载 Google Spreadsheet,请与服务帐户的电子邮件共享 Google Spreadsheet。这样,文件就可以被服务账号下载了。请注意这一点。您可以在服务帐户的凭据文件中看到它是client_email

示例脚本 1:

在使用之前,请设置credentialFileOfServiceAccountemailOfServiceAccountfile_id的变量。

import requests
from oauth2client.service_account import ServiceAccountCredentials

credentialFileOfServiceAccount = './###.json'
emailOfServiceAccount = '###'
file_id = '###'

creds = ServiceAccountCredentials.from_json_keyfile_name(credentialFileOfServiceAccount, scopes='https://www.googleapis.com/auth/drive.readonly')
access_token = creds.create_delegated(emailOfServiceAccount).get_access_token().access_token

url = 'https://www.googleapis.com/drive/v3/files/' + file_id + '/export?mimeType=text%2Fcsv'
headers = {'Authorization': 'Bearer ' + access_token}
res = requests.get(url, headers=headers)
print(res.text)

with open('Spam.csv', 'wb') as f:
    f.write(res.content)
  • 以上脚本为运行时,file_id的Spreadsheet导出为CSV数据,数据保存为Spam.csv的文件。在这种情况下,检索 Spreadsheet 的第一个选项卡的数据。这与您的脚本相同。

示例脚本 2:

在此示例脚本中,使用了 python 的 googleapis。

import io
from oauth2client.service_account import ServiceAccountCredentials
from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseDownload

credentialFileOfServiceAccount = './###.json'
file_id = '###'

creds = ServiceAccountCredentials.from_json_keyfile_name(credentialFileOfServiceAccount, ['https://www.googleapis.com/auth/drive.readonly'])
service = build('drive', 'v3', credentials=creds)
request = service .files().export_media(fileId=file_id, mimeType='text/csv')
fh = io.FileIO('Spam.csv', mode='wb')
downloader = MediaIoBaseDownload(fh, request)
done = False
while done is False:
    status, done = downloader.next_chunk()
    print('Download %d%%.' % int(status.progress() * 100))

最后一个后续问题的答案:

在上面的示例脚本中,第一个选项卡导出为 CSV 数据。为了导出除第一个选项卡 Google Spreadsheet 之外的 sheets,在此答案中,我想建议更改端点。我认为这种方式可能更简单。示例脚本如下

示例脚本:

使用前,请设置credentialFileOfServiceAccountemailOfServiceAccountfile_id变量。另外,请设置要导出的 sheet 名称。

import requests
from oauth2client.service_account import ServiceAccountCredentials

credentialFileOfServiceAccount = './###.json'
emailOfServiceAccount = '###'
file_id = '###'
sheet_name = 'Sheet2' # <--- Please set the sheet name you want to export.

creds = ServiceAccountCredentials.from_json_keyfile_name(credentialFileOfServiceAccount, scopes='https://www.googleapis.com/auth/drive.readonly')
access_token = creds.create_delegated(emailOfServiceAccount).get_access_token().access_token

url = 'https://docs.google.com/spreadsheets/d/' + file_id + '/gviz/tq?tqx=out:csv&sheet=' + sheet_name
headers = {'Authorization': 'Bearer ' + access_token}
res = requests.get(url, headers=headers)
print(res.text)

with open('Spam.csv', 'wb') as f:
    f.write(res.content)
  • 如果要使用sheetID(gid),端点如下

      url = 'https://docs.google.com/spreadsheets/d/' + file_id + '/gviz/tq?tqx=out:csv&gid=' + gid
    
  • 当上面的脚本为运行时,file_id的Spreadsheet中的“Sheet2”导出为CSV数据,数据保存为Spam.csv.

    的文件