自动化pydrive验证过程

83 投票
7 回答
59207 浏览
提问于 2025-04-18 11:06

我正在尝试在使用 pydrive 库时自动化 GoogleAuth 的过程(可以在这里找到这个库:https://pypi.python.org/pypi/PyDrive)。

我已经设置好了 pydrive 和谷歌 API,确保我的 secret_client.json 文件可以正常工作,但每次运行我的脚本时,它都需要进行网页认证才能访问谷歌云盘:

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 的那个谷歌云盘账户,而是上传到我在授权认证时登录的账户

根据之前的帖子,我尝试了以下方法来自动化验证过程,但出现了错误信息:

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')

错误:

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')]

我在谷歌云盘 API 上的认证看起来是这样的:

在这里输入图片描述

我该如何使用 pydrive,以便每次使用时都不需要进行认证?

如何实现自动认证,使得使用 pydrive 的 Python 脚本只会上传到生成 secret_client.json 的账户,而不是当前在网络浏览器中登录的账户?

7 个回答

2

我之前也遇到过类似的问题,后来发现我漏掉了一步,就是没有把自己添加为这个API的测试用户。

在Google Cloud里,找到“API和服务”然后点击“OAuth同意”。往下滚动,直到看到“测试用户”,然后把你的Drive的Gmail账号加进去。

这是Google Cloud测试用户的截图

1

5

这只是为了补充一下 @wang892 的 帖子(因为我没有足够的声望来评论)。

那个回答帮我自动化了我的脚本(这样每次运行时就不用重新认证了)。

但是因为我使用了 PyDrive 文档中提供的示例 settings.yaml 文件,所以遇到了一些问题(因为我对 oauth 的工作原理完全不懂)。

这个示例文件包含了这些行,我觉得这限制了我的 PyDrive 脚本只能访问它自己创建的文件和文件夹(详细信息请见 PyDrive 问题 #122):

有限访问:

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

当我修改了这些行后,问题就解决了(我需要删除存储的凭据,然后再次运行脚本进行重新授权,只需一次)。

有了这些新行,我的脚本现在可以访问我 Google Drive 中的所有文件:

完全访问:

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

关于这方面的更多信息可以在 PyDrive 问题 #108 中找到,这让我受益匪浅。

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,并在 GoogleAuth 的流程参数中将 access_type 设置为离线(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)

谢谢大家!

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文件,里面会有一个刷新令牌。

如果你想在服务器上自动运行你的脚本,这种方法会更有效。

153

首先,你对这个过程有一个很重要的误解:

当我尝试在登录另一个账户时使用上面的脚本,它并没有把 eng.txt 上传到我生成 secret_client.json 的 Google Drive,而是上传到了我授权时登录的那个账户。

这正是它应该工作的方式。作为开发者,你会把 client_secret.json 文件和你的应用程序一起分发,这个文件是 PyDrive 用来让你的应用程序和 Google 进行身份验证的。Google 想知道每个应用程序发出了多少 API 请求,这样他们可以出于各种原因(比如统计、收费、撤销访问权限等)进行管理,因此他们要求应用程序进行身份验证。

现在,当你的应用程序运行 LocalWebserverAuth 时,它是在用 Google 验证客户端。这里的客户端就是实际使用你应用程序的人。在这个例子中,开发者和客户端是同一个人(你),但想象一下,如果你想把你的应用程序分发给一百万个不同的人,他们需要能够自己进行身份验证,并把文件上传到自己的 Google Drive 账户,而不是都上传到你的账户(开发者的账户),因为你提供了 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')

撰写回答