Python YouTube数据API v3刷新令牌在发布状态设置为Testing的应用程序上持续过期
我想做什么: 我正试图构建一个Python 3.9程序,使用OAuth2凭据(设置为“测试”发布状态,作为“web应用程序”类型和“外部”用户类型)每天调用YouTube数据API v3,每次进行唯一调用时存储刷新令牌以获得新的访问令牌 我一直在使用,来自的Python代码示例,以及我从中找到的OAuth令牌解决方案 到目前为止我所尝试的: 这是我的Python代码(为了匿名起见,我对播放列表ID进行了置乱,但是如果您为一个具有凭据的频道输入自己的播放列表ID,代码运行良好): 我得到的结果: 我的程序运行了大约一周,然后我发现以下错误(同样,为了匿名,我编辑了部分文件路径):Python YouTube数据API v3刷新令牌在发布状态设置为Testing的应用程序上持续过期,python,google-api,google-oauth,youtube-data-api,google-api-python-client,Python,Google Api,Google Oauth,Youtube Data Api,Google Api Python Client,我想做什么: 我正试图构建一个Python 3.9程序,使用OAuth2凭据(设置为“测试”发布状态,作为“web应用程序”类型和“外部”用户类型)每天调用YouTube数据API v3,每次进行唯一调用时存储刷新令牌以获得新的访问令牌 我一直在使用,来自的Python代码示例,以及我从中找到的OAuth令牌解决方案 到目前为止我所尝试的: 这是我的Python代码(为了匿名起见,我对播放列表ID进行了置乱,但是如果您为一个具有凭据的频道输入自己的播放列表ID,代码运行良好): 我得到的结果:
/Users/../PycharmProjects/GoogleAPIs/helloyoutubedataapiouth.py
正在从文件加载凭据。。。
正在刷新访问令牌。。。
回溯(最近一次呼叫最后一次):
文件“/Users/../PycharmProjects/GoogleAPIs/helloyoutubedataapiouth.py”,第23行,在
凭据。刷新(请求())
文件“/Users/../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site packages/google/oauth2/credentials.py”,第200行,刷新
访问\u令牌、刷新\u令牌、到期、授予\u响应=\u client.refresh\u grant(
文件“/Users/../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site packages/google/oauth2/_client.py”,第248行,刷新授权
响应\数据=\令牌\端点\请求(请求,令牌\ uri,正文)
文件“/Users/../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site packages/google/oauth2/_client.py”,第124行,在_token_endpoint_请求中
_句柄错误响应(响应体)
文件“/Users/../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site packages/google/oauth2/_client.py”,第60行,在处理错误响应中
引发异常。刷新错误(错误详细信息、响应正文)
google.auth.exceptions.RefreshError:('invalid_grant:Bad Request','{\n“error”:“invalid_grant”,\n“error_description”:“Bad Request”\n}')
进程已完成,退出代码为1
我可以通过从我的目录中删除“youtube\u data\u token\u brand.pickle”文件并重新运行该程序(然后该程序要求我使用我的google帐户登录,并通过OAuth2步骤手动重新授权访问)来绕过这些错误
这让我相信我的刷新令牌即将过期(我发誓我在文档中的某个地方读到,在撤销访问权之前,它不应该过期——我还没有这样做——但在反复搜索之后,我再也找不到它了)
有趣的是,我能够为我控制的不同YouTube帐户运行相同的程序,并且该帐户没有遇到相同的刷新令牌错误问题。我还能够使用相同的token.pickle方法将刷新令牌存储到其他Google API(Google Analytics、YouTube Analytics等)他们中的任何一个都没有经历过这个问题
提前感谢您提供的任何帮助!根据,刷新令牌失效的一种情况是与相应刷新令牌关联的应用程序被撤销其操作权限
刷新令牌失效的另一种情况是,当相应的帐户已超过授予的最大刷新令牌数时:
当前,每个OAuth 2.0客户端ID的每个Google帐户的刷新令牌数限制为50个。如果达到该限制,创建新的刷新令牌会自动使最旧的刷新令牌失效,而不会发出警告
您可以检查一下是否确实发生了这种情况。刷新令牌可能会过期的原因有几个。主要原因是每次运行代码时,如果身份验证服务器返回一个新的刷新令牌,而您没有存储它,那么在运行五十次之后,您存储的刷新令牌将过期 注意:auth服务器不会每次都返回一个新的刷新令牌,访问令牌会被刷新。这似乎是基于C#做的一些语言,php没有,我也不认为node做的。我还没有找到发生这种情况的原因,我怀疑它在库中,我不确定python库是否会这样做不管怎样,最好让它来处理事情 请看这段代码,它允许库处理刷新令牌的所有存储。您似乎在手动执行许多操作。这可能会导致或可能不会导致您的刷新令牌后悔
"""Hello YouTube API ."""
import argparse
from apiclient.discovery import build
import httplib2
from oauth2client import client
from oauth2client import file
from oauth2client import tools
SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']
CLIENT_SECRETS_PATH = 'client_secrets.json' # Path to client_secrets.json file.
def initialize_youtube():
"""Initializes the youtube service object.
Returns:
youtube an authorized youtube service object.
"""
# Parse command-line arguments.
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[tools.argparser])
flags = parser.parse_args([])
# Set up a Flow object to be used if we need to authenticate.
flow = client.flow_from_clientsecrets(
CLIENT_SECRETS_PATH, scope=SCOPES,
message=tools.message_if_missing(CLIENT_SECRETS_PATH))
# Prepare credentials, and authorize HTTP object with them.
# If the credentials don't exist or are invalid run through the native client
# flow. The Storage object will ensure that if successful the good
# credentials will get written back to a file.
storage = file.Storage('youtube.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = tools.run_flow(flow, storage, flags)
http = credentials.authorize(http=httplib2.Http())
# Build the service object.
youtube= build('youtube', 'v3', http=http)
return youtube
您可能还想确保用户没有通过他们的google帐户撤销您的访问权限,但是我假设您已经检查过了。在阅读了其中一个答案中发布的@stvar之后,问题似乎是这个特定的刷新令牌始终有一周的寿命。这只是因为我的情况是“完美风暴”:
唯一的解决方案似乎是发布OAuth同意屏幕应用程序。(请确保我没有试图隐藏您的帖子;只是我们似乎都对同一个标签感兴趣--。)抱歉,@DaImTo,但建议使用
o
/Users/…/PycharmProjects/GoogleAPIs/HelloYouTubeDataAPIOAuth.py
Loading Credentials From File...
Refreshing Access Token...
Traceback (most recent call last):
File "/Users/.../PycharmProjects/GoogleAPIs/HelloYouTubeDataAPIOAuth.py", line 23, in <module>
credentials.refresh(Request())
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/credentials.py", line 200, in refresh
access_token, refresh_token, expiry, grant_response = _client.refresh_grant(
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/_client.py", line 248, in refresh_grant
response_data = _token_endpoint_request(request, token_uri, body)
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/_client.py", line 124, in _token_endpoint_request
_handle_error_response(response_body)
File "/Users/.../PycharmProjects/GoogleAPIs/venv/lib/python3.9/site-packages/google/oauth2/_client.py", line 60, in _handle_error_response
raise exceptions.RefreshError(error_details, response_body)
google.auth.exceptions.RefreshError: ('invalid_grant: Bad Request', '{\n "error": "invalid_grant",\n "error_description": "Bad Request"\n}')
Process finished with exit code 1
"""Hello YouTube API ."""
import argparse
from apiclient.discovery import build
import httplib2
from oauth2client import client
from oauth2client import file
from oauth2client import tools
SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']
CLIENT_SECRETS_PATH = 'client_secrets.json' # Path to client_secrets.json file.
def initialize_youtube():
"""Initializes the youtube service object.
Returns:
youtube an authorized youtube service object.
"""
# Parse command-line arguments.
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[tools.argparser])
flags = parser.parse_args([])
# Set up a Flow object to be used if we need to authenticate.
flow = client.flow_from_clientsecrets(
CLIENT_SECRETS_PATH, scope=SCOPES,
message=tools.message_if_missing(CLIENT_SECRETS_PATH))
# Prepare credentials, and authorize HTTP object with them.
# If the credentials don't exist or are invalid run through the native client
# flow. The Storage object will ensure that if successful the good
# credentials will get written back to a file.
storage = file.Storage('youtube.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = tools.run_flow(flow, storage, flags)
http = credentials.authorize(http=httplib2.Http())
# Build the service object.
youtube= build('youtube', 'v3', http=http)
return youtube