所以我尝试用 python 3.8.5 编写这个程序,它与 Google 电子表格交互,但是当令牌过期时,我不知道如何刷新它。
我的代码几乎是从https://developers.google.com/sheets/api/quickstart/python复制的:
from __future__ import print_function
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
def service_spread():
"""
Creates token for spreadsheet with required authorisation.
Returns required service.
"""
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token_spread.json'):
creds = Credentials.from_authorized_user_file('token_spread.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token_spread.json', 'w') as token:
token.write(creds.to_json())
return build('sheets', 'v4', credentials=creds)
如果没有代币,它的作用就像一个符咒。如果存在令牌并且已过期,则会引发以下错误:
RefreshError Traceback (most recent call last)
<ipython-input-6-63b097a471a1> in <module>
----> 1 service = service_spread()
<ipython-input-5-12e029dbbb2f> in service_spread()
14 if not creds or not creds.valid:
15 if creds and creds.expired and creds.refresh_token:
---> 16 creds.refresh(Request())
17 else:
18 flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
~\anaconda3\lib\site-packages\google\oauth2\credentials.py in refresh(self, request)
206 scopes = self._scopes if self._scopes is not None else self._default_scopes
207
--> 208 access_token, refresh_token, expiry, grant_response = _client.refresh_grant(
209 request,
210 self._token_uri,
~\anaconda3\lib\site-packages\google\oauth2\_client.py in refresh_grant(request, token_uri, refresh_token, client_id, client_secret, scopes)
246 body["scope"] = " ".join(scopes)
247
--> 248 response_data = _token_endpoint_request(request, token_uri, body)
249
250 try:
~\anaconda3\lib\site-packages\google\oauth2\_client.py in _token_endpoint_request(request, token_uri, body)
122 retry += 1
123 continue
--> 124 _handle_error_response(response_body)
125
126 return response_data
~\anaconda3\lib\site-packages\google\oauth2\_client.py in _handle_error_response(response_body)
58 error_details = response_body
59
---> 60 raise exceptions.RefreshError(error_details, response_body)
61
62
RefreshError: ('invalid_scope: Bad Request', '{\n "error": "invalid_scope",\n "error_description": "Bad Request"\n}')
我在网上查遍了,但不知道如何解决。有人可以帮助我吗?我需要此代码在无人监督的情况下运行,因此我需要自动刷新令牌,无需任何人为干预。
我偶然发现了这个,我也遇到了同样的问题并修复了它。他们检查令牌是否过期的逻辑一定有问题。但我添加了手动检查来做到这一点。在
token.json
中有一个 expiry
字段,显示令牌将在何时过期。我只是手动检查当前时间是否超过该时间。如果是这样,我自己删除该文件。我的验证码如下。
def auth(self):
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first time.
if os.path.exists("token.json"):
token = json.load(open("token.json"))
expiry = datetime.fromisoformat(token["expiry"])
if expiry < datetime.now(expiry.tzinfo):
self.logger.info(
"Token expired, removing token.json and re-authenticating"
)
os.remove("token.json")
creds = None
else:
self.logger.info("Reading credentials from existing token.json")
creds = Credentials.from_authorized_user_file("token.json", SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
self.logger.info("Credentials expired, sending refresh request")
creds.refresh(Request())
else:
self.logger.info(
"Credentials not found, sending auth request to local server"
)
flow = InstalledAppFlow.from_client_secrets_file(
"credentials.json", SCOPES
)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open("token.json", "w") as token:
self.logger.info("Saving credentials to token.json")
token.write(creds.to_json())
self.service = build("gmail", "v1", credentials=creds)
self.logger.info("Completed Gmail authentication flow")