自动化pydrive验证流程

83

我正在尝试在使用PyDrive库 (https://pypi.python.org/pypi/PyDrive) 时自动化 GoogleAuth 过程。

我已经设置好了PyDrive和Google API,以使我的secret_client.json可以工作,但每次运行脚本时都需要进行Web身份验证以访问gdrive:

from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive

gauth = GoogleAuth()
gauth.LocalWebserverAuth()

drive = GoogleDrive(gauth)

textfile = drive.CreateFile()
textfile.SetContentFile('eng.txt')
textfile.Upload()
print textfile

drive.CreateFile({'id':textfile['id']}).GetContentFile('eng-dl.txt')

eng.txt只是一个文本文件。此外,当我尝试在另一个帐户中登录时使用上述脚本时,它不会将eng.txt上传到生成了secret_client.json的gdrive,而是上传到授权身份验证时登录的账户。

从以前的帖子中,我尝试了以下方法来自动化验证过程,但它给出了错误信息:

import base64, httplib2
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive

from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive

#gauth = GoogleAuth()
#gauth.LocalWebserverAuth()

# from google API console - convert private key to base64 or load from file
id = "464269119984-j3oh4aj7pd80mjae2sghnua3thaigugu.apps.googleusercontent.com"
key = base64.b64decode('COaV9QUlO1OdqtjMiUS6xEI8')

credentials = SignedJwtAssertionCredentials(id, key, scope='https://www.googleapis.com/auth/drive')
credentials.authorize(httplib2.Http())

gauth = GoogleAuth()
gauth.credentials = credentials

drive = GoogleDrive(gauth)

drive = GoogleDrive(gauth)

textfile = drive.CreateFile()
textfile.SetContentFile('eng.txt')
textfile.Upload()
print textfile

drive.CreateFile({'id':textfile['id']}).GetContentFile('eng-dl.txt')
Error:
Traceback (most recent call last):
  File "/home/alvas/git/SeedLing/cloudwiki.py", line 29, in <module>
    textfile.Upload()
  File "/usr/local/lib/python2.7/dist-packages/pydrive/files.py", line 216, in Upload
    self._FilesInsert(param=param)
  File "/usr/local/lib/python2.7/dist-packages/pydrive/auth.py", line 53, in _decorated
    self.auth.Authorize()
  File "/usr/local/lib/python2.7/dist-packages/pydrive/auth.py", line 422, in Authorize
    self.service = build('drive', 'v2', http=self.http)
  File "/usr/local/lib/python2.7/dist-packages/oauth2client/util.py", line 132, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/apiclient/discovery.py", line 192, in build
    resp, content = http.request(requested_url)
  File "/usr/local/lib/python2.7/dist-packages/oauth2client/util.py", line 132, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/oauth2client/client.py", line 475, in new_request
    self._refresh(request_orig)
  File "/usr/local/lib/python2.7/dist-packages/oauth2client/client.py", line 653, in _refresh
    self._do_refresh_request(http_request)
  File "/usr/local/lib/python2.7/dist-packages/oauth2client/client.py", line 677, in _do_refresh_request
    body = self._generate_refresh_request_body()
  File "/usr/local/lib/python2.7/dist-packages/oauth2client/client.py", line 861, in _generate_refresh_request_body
    assertion = self._generate_assertion()
  File "/usr/local/lib/python2.7/dist-packages/oauth2client/client.py", line 977, in _generate_assertion
    private_key, self.private_key_password), payload)
  File "/usr/local/lib/python2.7/dist-packages/oauth2client/crypt.py", line 131, in from_string
    pkey = crypto.load_pkcs12(key, password).get_privatekey()
OpenSSL.crypto.Error: [('asn1 encoding routines', 'ASN1_get_object', 'header too long')]

我的gdrive api身份验证如下:

enter image description here

我该如何使用PyDrive,以便每次使用它时都不需要进行身份验证?

如何允许自动身份验证,以使使用PyDrive脚本的python脚本仅上传到生成secret_client.json的帐户,而不是当前在互联网浏览器上登录的帐户?

7个回答

153

首先,你误解了其中一个非常重要的细节:

当我尝试在另一个帐户中登录时使用上述脚本时,它不会将 eng.txt 上传到生成 secret_client.json 的我的gdrive中,而是上传到授权身份验证时已登录的帐户。

这正是它应该工作的方式。你作为开发者,与你的应用程序一起分发 client_secret.json文件,并且该文件由 PyDrive 用于对 Google 进行身份验证。Google 想知道各个应用程序正在进行多少 API 请求,出于各种原因(指标、收费账户、撤销访问权限等),因此它要求应用程序对自身进行身份验证。

现在,当你的应用程序运行 LocalWebserverAuth 时,它正在对 Google 进行客户端身份验证。客户端,当然,就是实际使用你的应用程序的人。在这种情况下,开发者和客户端是同一个人(你),但想象一下,你想将你的应用程序分发给数百万不同的人。他们需要能够对自己进行身份验证并将文件上传到自己的驱动器帐户中,而不是让它们全部进入你(开发者)提供的 client_secret.json 中。

话虽如此,只需进行非常小的更改即可使你的应用程序不必每次运行应用程序时都要求客户端进行身份验证。你只需要使用LoadCredentialsFileSaveCredentialsFile

from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive

gauth = GoogleAuth()
# Try to load saved client credentials
gauth.LoadCredentialsFile("mycreds.txt")
if gauth.credentials is None:
    # Authenticate if they're not there
    gauth.LocalWebserverAuth()
elif gauth.access_token_expired:
    # Refresh them if expired
    gauth.Refresh()
else:
    # Initialize the saved creds
    gauth.Authorize()
# Save the current credentials to a file
gauth.SaveCredentialsFile("mycreds.txt")

drive = GoogleDrive(gauth)

textfile = drive.CreateFile()
textfile.SetContentFile('eng.txt')
textfile.Upload()
print textfile

drive.CreateFile({'id':textfile['id']}).GetContentFile('eng-dl.txt')

1
mycreds.txt文件中需要包含哪些信息?是否仍需要client_secrets.json文件?当我使用上述代码与client_secrets.json文件时,它仍然会请求验证代码,或者我完全错过了什么? - Gregg
8
@Gregg,您仍需要client_secrets.json来对应用进行身份验证。mycreds.txt将存储可以用于对用户进行身份验证的令牌。因此,第一次运行此代码时,您需要通过Web浏览器输入验证代码。第二次运行时,它应该使用保存在mycreds.txt中的凭据。 - dano
6
这种方法与@wang892的方法相比如何?令牌过期后我需要手动重新授权吗? - avg
1
我个人必须要有一个settings.yaml文件,其中包含get_refresh_token: True,才能使其正常运作。 - agenis
2
@sh37211,这只是问题提出者试图上传的文件,与身份验证过程无关。 - dano
显示剩余7条评论

24

另一种方法是通过编写一个setting.yaml文件到工作目录来使用自定义的认证流程。这种方法更好,因为LocalWebserverAuth()将生成一个仅在一小时内过期且没有刷新令牌的令牌。

样例settings.yaml文件如下:

client_config_backend: file
client_config:
    client_id: <your_client_id>
    client_secret: <your_secret>

save_credentials: True
save_credentials_backend: file
save_credentials_file: credentials.json

get_refresh_token: True

oauth_scope:
    - https://www.googleapis.com/auth/drive
    - https://www.googleapis.com/auth/drive.install

使用此文件,您仍需要使用浏览器完成首次身份验证,之后将在工作目录中生成一个credentials.json文件和一个刷新令牌。

如果您正在尝试在服务器上自动化脚本,则此方法更有效。


1
嗨 - 我刚开始接触这个东西 - 按照快速入门并添加了您非常有帮助的建议。我注意到 'setting.yaml' 应该是 'settings.yaml' - 然后它就很好地工作了。 - Townheadbluesboy
2
谢谢,这个像魔法一样好用!PyDrive快速入门指南的细节不是很清晰。 - avg
4
请提供类似于dano上面提供的示例代码,展示您的身份验证代码是如何实际工作的? - James Shapiro

23
这整个帖子对我有很大帮助,但在我实现这里提出的所有解决方案后,又遇到了一个问题:LocalWebserverAuth()无法获取刷新令牌
如果你打开实现@dano代码后生成的“mycreds.txt”,你会发现“刷新令牌”将被设置为“null”。几个小时后,令牌过期并且您会得到以下错误,最终不得不手动再次进行验证。
错误信息:
raise RefreshError('No refresh_token found.') pydrive.auth.RefreshError: No refresh_token found.Please set access_type of OAuth to offline.
那个问题的解决方案是强制 approval_prompt 并将 access_type 设置为 GoogleAuth 的流参数中的 offline。
这是我不再出现错误的方法:
gauth = GoogleAuth()

# Try to load saved client credentials
gauth.LoadCredentialsFile("mycreds.txt")

if gauth.credentials is None:
    # Authenticate if they're not there

    # This is what solved the issues:
    gauth.GetFlow()
    gauth.flow.params.update({'access_type': 'offline'})
    gauth.flow.params.update({'approval_prompt': 'force'})

    gauth.LocalWebserverAuth()

elif gauth.access_token_expired:

    # Refresh them if expired

    gauth.Refresh()
else:

    # Initialize the saved creds

    gauth.Authorize()

# Save the current credentials to a file
gauth.SaveCredentialsFile("mycreds.txt")  

drive = GoogleDrive(gauth)

谢谢大家!


添加access_type:offline和approval_prompt:force让我感到非常开心!非常感谢你!太棒了。顺便问一下,Gauth Flow的文档在哪里?我找不到它... - pmalbu
这三行代码可以被一行代码替代:gauth.settings.update({'get_refresh_token': True}). 这种可能性可以在文件https://github.com/iterative/PyDrive2/blob/main/pydrive2/auth.py#L650C50-L650C50中找到。 - undefined

5

这只是为了完成@wang892在上述帖子中的内容(我没有足够的声望来评论)。

那个答案帮助我自动化我的脚本(不需要每次运行时重新进行身份验证)。

但是由于我完全不了解oauth的工作原理,所以当我使用PyDrive文档中提供的示例settings.yaml文件时,遇到了一些问题。

该示例文件包含以下行,我认为它们限制了我的PyDrive脚本仅访问由其自己创建的文件和文件夹(有关详细信息,请参见PyDrive问题#122):

有限访问:

oauth_scope:
  - https://www.googleapis.com/auth/drive.file
  - https://www.googleapis.com/auth/drive.install

当我更改了这些行之后,问题得到了解决(我不得不删除我的存储凭据并运行脚本重新授权,再次执行一次)。
有了这些新的行,我的脚本现在可以访问我谷歌云盘中的所有文件:
完全访问:
oauth_scope:
  - https://www.googleapis.com/auth/drive

更多关于这个问题的信息可以在PyDrive issue #108中找到,这对我启发很大。


2

我曾经遇到过类似的问题,最后发现我忘了将自己添加为API的测试用户。

在Google Cloud中,进入API和服务 > OAuth同意页面。向下滚动直到找到“测试用户”,然后添加您的Gmail帐户。

Google Cloud测试用户截图

1


1
如果凭证未设置,此代码将生成一个带有两个选项的输入框:
  • 浏览器身份验证(只需执行一次)

  • 上传凭证文件(此文件将在您首次选择浏览器身份验证时生成)

现在可以轻松共享笔记本,它将直接运行而无需授权,因为它将使用保存在本地环境中的mycreds.txt中的凭据。但是,如果运行时崩溃或重置,该文件将丢失,并需要通过上面的输入框重新插入。当然,您可以通过浏览器身份验证再次执行此操作,但如果您将mycreds.txt重新分发给正在使用笔记本电脑的人,则他们可以使用上传功能将凭证插入到本地环境中。
最后几行提供了一个示例,说明如何上传经过身份验证的驱动器中的csv文件并在笔记本中使用。
#Install the required packages and fix access to my Google drive account
!pip install pydrive
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
from google.colab import auth
from oauth2client.client import GoogleCredentials


#Checks for file with Google authentication key, if the file is not in place, it asks to authenticate via the browser
gauth = GoogleAuth()
if os.path.isfile("mycreds.txt") is False:
    choice = input ("Do you want to: U) Upload authentication file (mycreds.txt). B) Browser authentication (only possible for owner of the connected Google drive folder). [U/B]? : ")
    if choice == "U":
          print ("Upload the mycreds.txt file")
          from google.colab import files
          files.upload()      
    elif choice == "B":
          auth.authenticate_user()
          gauth.credentials = GoogleCredentials.get_application_default()
          gauth.SaveCredentialsFile("mycreds.txt")

gauth.LoadCredentialsFile("mycreds.txt")
if gauth.access_token_expired:
    gauth.Refresh()
else: gauth.Authorize()

#Now you can easily use the files from your drive by using their ID  
drive = GoogleDrive(gauth)
download = drive.CreateFile({'id': '1KRqYpR9cteX-ZIwhdfghju6_wALl4'})
download.GetContentFile('my_data.csv')
data_frame = pd.read_csv('my_data.csv')

1
这几乎是我需要的,但认证部分由于某些原因无法工作。我可以毫无问题地生成mycreds.txt,但然后脚本就没有消息,也没有挂载谷歌驱动器。 - Sulli

0
基于"dano和tetodenega"的回答,这是一个更灵活的版本。
允许您自定义client_secret.json和mycreds.txt的路径。并且可以防止凭据在24小时内过期。

    from pydrive2.auth import GoogleAuth
    from pydrive2.drive import GoogleDrive
    from pathlib import Path
    
    gauth = GoogleAuth()
    # optional for custom path of client_secret.json
    # v1-To set a default default in pydrive2
    # gauth.DEFAULT_SETTINGS['client_config_file'] = 'your_path_of_/client_secret.json')
    # v2-To config this for this runtime
    # gauth.settings.update({'client_config_file': 'your_path_of_/client_secret.json'})
    
    mycreds_path = "mycreds.txt file path. Put it in home/.google for security"
    if Path(credentials.mycreds).exists() is False:
        # Authenticate if they're not there
        gauth.DEFAULT_SETTINGS['get_refresh_token'] = True
        gauth.LocalWebserverAuth()
        drive = GoogleDrive(gauth)
        gauth.SaveCredentialsFile(mycreds_path)
    else:
        gauth.LoadCredentialsFile(mycreds_path)
        if gauth.access_token_expired:
            gauth.Refresh()
        else:
            gauth.Authorize()
            drive = GoogleDrive(gauth)

如何工作: 在浏览器中进行首次登录时需要确认。 首次登录后,将不再要求在浏览器中进行确认。 因此,将使用存储在安全文件夹中的mycreds.txt中的凭据进行未来的登录。
要获取您的client_secret.json,请阅读PyDrive2快速入门中的身份验证主题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接