可重复使用的Google表格库:HttpError 404

时间:2017-03-08 18:13:28

标签: python google-api-python-client google-sheets-api

使用Python Quickstart example for the v4 Google Sheets API作为起点,我尝试使用readwrite函数创建一个库,然后可以让更高级别的类轻松使用与我的表格互动。这仅在我将库本身用作调用读/写函数的脚本时才有效。如果我在导入位于不同目录中的外部脚本后使用它们,则readwrite都会引发以下错误:

请求https://sheets.googleapis.com/ $ discovery / v4 / spreadsheets /

HttpError 404

该网址看起来格式不正确" $ discovery"在里面。

这是我的图书馆,其中包含部分,如果此库作为脚本运行,该图片效果很好:

# sheetlib.py
""" Google Docs Spreadsheets wrapper
"""

import httplib2
import os
import json
json.JSONEncoder.default=str

from apiclient import discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage


SCOPES = 'https://www.googleapis.com/auth/spreadsheets'
CLIENT_SECRET_FILE = 'client_secret.json'
CREDENTIAL_PATH = 'sheets.googleapis.test.json'
APPLICATION_NAME = 'Test Sheet'
SPREADSHEET_ID = 'abcdefg'


def get_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """
    store = Storage(CREDENTIAL_PATH)
    print 'Environment: {}'.format(json.dumps(os.environ.__dict__['data']))
    print 'Loaded store from {}: {}'.format(CREDENTIAL_PATH, json.dumps(store.__dict__))
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        credentials = tools.run_flow(flow, store)
        print 'Storing credentials to ' + CREDENTIAL_PATH
    return credentials


def build_service():
    """ Returns service object for reading/writing to spreadsheet """
    credentials = get_credentials()
    print "credentials: {}".format(json.dumps(credentials.__dict__))
    http = credentials.authorize(httplib2.Http())
    discoveryUrl = ('https://sheets.googleapis.com/$discovery/rest?version=v4')
    service = discovery.build('sheets', 'v4', http=http, discoveryServiceUrl=discoveryUrl)
    print 'service: {}'.format(json.dumps(service._http.__dict__))
    return service


def write(range, values):
    service = build_service()
    body = {
        'values': values
    }
    service.spreadsheets().values().append(
        spreadsheetId=SPREADSHEET_ID, range=range,
        valueInputOption='RAW', body=body, insertDataOption='INSERT_ROWS').execute()


def read(range):
    """ Pass a range to read, like 'RawData!A:E' """
    service = build_service()
    resp = service.spreadsheets().values().get(spreadsheetId=SPREADSHEET_ID, range=range).execute()
    return resp


class Magic():
    """Reads and writes to the Magic tab of sheet"""

    def spell_list(self):
        return [r for r in read('Magic!A1:G100')['values'][1:]]


if __name__ == '__main__':
    m = Magic()
    print m.spell_list()

如果我将Magic类移动到位于的另一个目录中的另一个文件并尝试使用导入的read,则会抛出404错误:

# magic_test.py
from sheetlib import read
class BadMagic():
    """Reads and writes to the Magic tab of sheet"""

    def spell_list(self):
         return [r for r in read('Magic!A1:G100')['values'][1:]]
m = BadMagic()
m.spell_list()

Traceback (most recent call last):
  File "magic_test.py", line 0, in main
    return [r[0] for r in read('Magic!A2:A100')['values']]
  File "sheetlib.py", line 0, in read
    resp = service.spreadsheets().values().get(spreadsheetId=SPREADSHEET_ID, range=range).execute()
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/google-api-python-client/apiclient/http.py", line 292, in execute
    raise HttpError(resp, content, self.uri)
apiclient.errors.HttpError: <HttpError 404 when requesting https://sheets.googleapis.com/$discovery/v4/spreadsheets/abcdefg/values/Magic%21A2%3AA100?alt=json returned "Not Found">

进一步探索,我发现credentials:函数的service:build_service()输出会有所不同,具体取决于使用它的脚本:

来自sheetlib.py(正在工作)

凭证:

{
  "scopes": "set([u'https://www.googleapis.com/auth/spreadsheets'])",
  "revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
  "access_token": "asdf",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "token_info_uri": "https://www.googleapis.com/oauth2/v3/tokeninfo",
  "token_response": {
    "access_token": "asdf",
    "token_type": "Bearer",
    "expires_in": 3600
  },
  "invalid": false,
  "refresh_token": "qwer",
  "client_id": "1234.apps.googleusercontent.com",
  "id_token": null,
  "client_secret": "zxcv",
  "token_expiry": "2017-03-08 17:01:42",
  "store": "<oauth2client.file.Storage object at 0x10bbd6690>",
  "user_agent": "Magic Sheet"
}

服务:

{
  "force_exception_to_status_code": false,
  "forward_authorization_headers": false,
  "authorizations": [],
  "proxy_info": "<function proxy_info_from_environment at 0x10af9aed8>",
  "follow_redirects": true,
  "cache": null,
  "request": "<function new_request at 0x10b3dba28>",
  "connections": {},
  "certificates": "<httplib2.KeyCerts object at 0x10b3df3d0>",
  "optimistic_concurrency_methods": [
    "PUT",
    "PATCH"
  ],
  "follow_all_redirects": false,
  "timeout": null,
  "ignore_etag": false,
  "ca_certs": null,
  "credentials": "<httplib2.Credentials object at 0x10b3df410>",
  "disable_ssl_certificate_validation": false
}

从magic_test.py拨打电话(已损坏)

凭证:

{
  "access_token": "asdf",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "invalid": false,
  "refresh_token": "qwer",
  "client_id": "1234.apps.googleusercontent.com",
  "id_token": null,
  "client_secret": "zxcv",
  "token_expiry": "2017-03-08 17:01:42",
  "store": "<oauth2client.file.Storage object at 0x1101a2e50>",
  "user_agent": "Accounting Sheet"
}

服务:

{
  "force_exception_to_status_code": false,
  "forward_authorization_headers": false,
  "authorizations": [],
  "proxy_info": "<bound method type.from_environment of <class 'httplib2.ProxyInfo'>>",
  "follow_redirects": true,
  "cache": null,
  "request": "<function new_request at 0x1101bae60>",
  "connections": {
    "https:sheets.googleapis.com": "<httplib2.HTTPSConnectionWithTimeout instance at 0x1101b1ea8>"
  },
  "certificates": "<httplib2.KeyCerts object at 0x1101c2890>",
  "optimistic_concurrency_methods": [
    "PUT",
    "PATCH"
  ],
  "follow_all_redirects": false,
  "timeout": null,
  "ignore_etag": false,
  "ca_certs": null,
  "credentials": "<httplib2.Credentials object at 0x1101c28d0>",
  "disable_ssl_certificate_validation": false
}

为什么会根据哪个脚本调用http2lib来使用...的不同部分?

1 个答案:

答案 0 :(得分:0)

您可以参考此thread。请注意,arg中包含的正文update()虽然在文档中显示为json,但实际上需要是常规的python字典。此外,您的404错误意味着您请求的内容不存在(或者它不希望您知道存在的内容)。这是一个article,它可以帮助您解决404错误。