【问题标题】:"'Credentials' object has no attribute 'access_token'" when using google-auth with gspread将 google-auth 与 gspread 一起使用时,“'Credentials' 对象没有属性 'access_token'”
【发布时间】:2019-01-08 03:07:36
【问题描述】:

我想使用gspread 模块从 Python 编辑 Google 表格。 setup instructions 包含以下示例:

import gspread
from oauth2client.service_account import ServiceAccountCredentials

scope = ['https://spreadsheets.google.com/feeds',
         'https://www.googleapis.com/auth/drive']

credentials = ServiceAccountCredentials.from_json_keyfile_name('gspread-april-2cd … ba4.json', scope)

gc = gspread.authorize(credentials)

但是,根据https://pypi.org/project/oauth2client/oauth2client 库已被弃用。因此,我尝试使用google-auth 进行如下调整:

import gspread
from google.oauth2 import service_account

credentials = service_account.Credentials.from_service_account_file(
    'my_client_secrets.json')

scoped_credentials = credentials.with_scopes(
    ['https://www.googleapis.com/auth/spreadsheets'])

gc = gspread.authorize(scoped_credentials)

不幸的是,我遇到了以下错误:

(lucy-web-CVxkrCFK) bash-3.2$ python nps.py
Traceback (most recent call last):
  File "nps.py", line 54, in <module>
    gc = gspread.authorize(scoped_credentials)
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/gspread/__init__.py", line 38, in authorize
    client.login()
  File "/Users/kurtpeek/.local/share/virtualenvs/lucy-web-CVxkrCFK/lib/python3.7/site-packages/gspread/client.py", line 46, in login
    if not self.auth.access_token or \
AttributeError: 'Credentials' object has no attribute 'access_token'

如果我进入调试器,我确实看到 credentials 有一个 token 属性,但没有一个 access_token 属性:

> /Users/kurtpeek/Documents/Dev/lucy2/lucy-web/scripts/nps.py(54)<module>()
     53 import ipdb; ipdb.set_trace()
---> 54 gc = gspread.authorize(scoped_credentials)
     55 

ipdb> type(credentials)
<class 'google.oauth2.service_account.Credentials'>
ipdb> type(scoped_credentials)
<class 'google.oauth2.service_account.Credentials'>
ipdb> dir(credentials)
['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_impl', '_additional_claims', '_from_signer_and_info', '_make_authorization_grant_assertion', '_project_id', '_scopes', '_service_account_email', '_signer', '_subject', '_token_uri', 'apply', 'before_request', 'expired', 'expiry', 'from_service_account_file', 'from_service_account_info', 'has_scopes', 'project_id', 'refresh', 'requires_scopes', 'scopes', 'service_account_email', 'sign_bytes', 'signer', 'signer_email', 'token', 'valid', 'with_claims', 'with_scopes', 'with_subject']

google-auth生成的Credentialsoauth2client生成的对象不是同一个对象吗?

【问题讨论】:

    标签: python google-api google-authentication oauth2client


    【解决方案1】:

    根据gspread 文档,gspread.authorize 方法仅支持由oauth2client library 创建的凭据对象。要使用新的google-auth,gspread 应该添加对它的支持。

    如果您不想使用已弃用的 oauthclient2,一个可能的解决方法是使用 authlib,利用 gspread.Client 类的会话参数。有一个很好的教程来说明如何做到这一点here

    2020 年 4 月 27 日更新

    version 3.4.0 开始,gspread 现在支持google-auth。您可以在dedicated documentation 中找到所有详细信息。以下是作者的官方声明:

    旧版本的 gspread 使用了oauth2client。谷歌有 deprecated 它支持 google-auth。如果您仍在使用 oauth2client 凭据,该库将为您将这些凭据转换为 google-auth,但您可以更改代码以使用新凭据,以确保将来不会出现任何问题。

    【讨论】:

    • 自发布以来,GSpread 已添加对新 google-auth 凭据的访问权限:gspread.readthedocs.io/en/latest/oauth2.html
    • @Alec 是正确的。 gspread 自 3.4.0 版以来一直在使用 google-auth。
    • @Burnash 和 Alec(Stackoverflow 只允许我有一个标签)感谢您指出新闻。我已经相应地更新了我的答案,如果你觉得不错,请告诉我。
    【解决方案2】:

    一年半后,在完全相同的情况下,我发现这对我有用:

    import gspread
    from google.oauth2 import service_account
    from google.auth.transport.requests import AuthorizedSession
    
    credentials = service_account.Credentials.from_service_account_file(
        'your_key_file.json')
    
    scoped_credentials = credentials.with_scopes(
            ['https://spreadsheets.google.com/feeds',
             'https://www.googleapis.com/auth/drive']
            )
    
    gc = gspread.Client(auth=scoped_credentials)
    gc.session = AuthorizedSession(scoped_credentials)
    sheet = gc.open_by_key('key_in_sharelink')
    print(sheet.title)
    

    解决方案改编自 gspread github 中的 this postgoogle-auth user guide

    【讨论】:

    • 它也适用于我用 google.oauth2 替换 oauth2client 使用。谢谢!
    【解决方案3】:

    作为一种临时修复方法,您可以创建一个继承凭据的新类,并添加一个新的access_token 属性,以反映凭据中的令牌。然后将其传递给gspread.authorize,它应该可以工作。

    # create the new class to fix credentials
    class fixed_creds(service_account.Credentials):
        def __init__(self, creds):
            self.access_token = creds.token
    
    # create new credential object with the access_token
    gcreds = fixed_creds(credentials)
    
    # pass new credentials into gspread
    sheets = gspread.authorize(gcreds)
    
    # create a new sheet to test it
    new_sheet = sheets.create('TestSheet')
    
    # give yourself permission to see the new sheet
    sheets.insert_permission(
        new_sheet.id,
        'youremail@yourdomain.com',
        perm_type='user',
        role='writer'
    )
    

    【讨论】:

      【解决方案4】:

      Tom 的创可贴修复对我不起作用,因为 token 在 Google OAuth2 库中最初是 None。这是我的创可贴修复:

      import gspread
      import google.auth.transport.requests
      from google.oauth2 import service_account
      from oauth2client.service_account import ServiceAccountCredentials
      
      class OAuth2ServiceAccountFromGoogleOAuth2(ServiceAccountCredentials):  # Hack based upon https://stackoverflow.com/questions/51618127/credentials-object-has-no-attribute-access-token-when-using-google-auth-wi
          def __init__(self, google_oauth2_credentials):
              self.google_oauth2_credentials = google_oauth2_credentials
              self.access_token = google_oauth2_credentials.token
      
          def refresh(self, http):
              if self.access_token is None:
                  request = google.auth.transport.requests.Request()
                  self.google_oauth2_credentials.refresh(request)
                  self.access_token = self.google_oauth2_credentials.token
              #end if
      
              print(f'access token in {self.access_token}')
          #end def
      #end class
      
      with open("credentials.json") as gs_key_file:
          google_credentials = service_account.Credentials.from_service_account_info(json.loads(gs_key_file.read()), scopes=['https://www.googleapis.com/auth/spreadsheets'])
      gclient = gspread.authorize(OAuth2ServiceAccountFromGoogleOAuth2(google_credentials))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-12-01
        • 1970-01-01
        • 2022-07-04
        • 2022-12-15
        • 2015-09-07
        • 1970-01-01
        • 2015-01-16
        • 2023-03-22
        相关资源
        最近更新 更多