每次使用 YouTube Data API v3 时如何绕过输入验证码来授权我的代码

Posted

技术标签:

【中文标题】每次使用 YouTube Data API v3 时如何绕过输入验证码来授权我的代码【英文标题】:How to bypass entering authentication code to authorize my code everytime I use the YouTube Data API v3 【发布时间】:2021-02-19 10:57:01 【问题描述】:

因此,每次我运行我的代码时,它都会在我的终端上提供一个链接,我必须手动按下该链接并在浏览器上选择我的 Gmail 帐户才能登录并接收授权码。我必须再次粘贴到我的终端上。

有没有办法跳过这个过程?

我正在使用的代码:

# -*- coding: utf-8 -*-

# Sample Python code for youtube.videos.update
# See instructions for running these code samples locally:
# https://developers.google.com/explorer-help/guides/code_samples#python

import os

import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors

scopes = ["https://www.googleapis.com/auth/youtube.force-ssl"]

def main():
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

    api_service_name = "youtube"
    api_version = "v3"

    client_secrets_file = "client_secret_key.json"

    # Get credentials and create an API client
    flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
        client_secrets_file, scopes)
    credentials = flow.run_console()
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, credentials=credentials)

    request = youtube.videos().update(
        part="id,snippet",
        body=
          "id": "videoid",
          "snippet": 
            "title": "XOXOXO",
            "description": "Through IDE",
            "categoryId": "27"
          
        
    )
    response = request.execute()

    print(response)

if __name__ == "__main__":
    main()

【问题讨论】:

【参考方案1】:

确实有可能在第一次成功运行 OAuth 授权/身份验证流程时保存您的 credentials 对象;然后在每次运行程序第n-th 次时从该文件加载凭据对象,其中n >= 2

以下是我推荐的代码结构:

import os, pickle

from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build

def pickle_file_name(
        api_name = 'youtube',
        api_version = 'v3'):
    return f'token_api_name_api_version.pickle'

def load_credentials(
        api_name = 'youtube',
        api_version = 'v3'):
    pickle_file = pickle_file_name(
        api_name, api_version)

    if not os.path.exists(pickle_file):
        return None

    with open(pickle_file, 'rb') as token:
        return pickle.load(token)

def save_credentials(
        cred, api_name = 'youtube',
        api_version = 'v3'):
    pickle_file = pickle_file_name(
        api_name, api_version)

    with open(pickle_file, 'wb') as token:
        pickle.dump(cred, token)

def create_service(
        client_secret_file, scopes,
        api_name = 'youtube',
        api_version = 'v3'):
    print(client_secret_file, scopes,
        api_name, api_version,
        sep = ', ')

    cred = load_credentials(api_name, api_version)

    if not cred or not cred.valid:
        if cred and cred.expired and cred.refresh_token:
            cred.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                    client_secret_file, scopes)
            cred = flow.run_console()

    save_credentials(cred, api_name, api_version)

    try:
        service = build(api_name, api_version, credentials = cred)
        print(api_name, 'service created successfully')
        return service
    except Exception as e:
        print(api_name, 'service creation failed:', e)
        return None

def main():
    youtube = create_service("client_secret_key.json",
        ["https://www.googleapis.com/auth/youtube.force-ssl"])
    if not youtube: return

    request = youtube.videos().update(
        part="id,snippet",
        body=
          "id": "videoid",
          "snippet": 
            "title": "XOXOXO",
            "description": "Through IDE",
            "categoryId": "27"
          
        
    )
    response = request.execute()

    print(response)

if __name__ == '__main__':
    main()

您必须注意上述代码的以下特点:如果您第二次从与第一次运行的目录不同的目录中运行脚本,则脚本将重新启动 OAuth当该(当前)目录不包含凭据泡菜文件时流动。


现在,如果您已经安装(或愿意安装)软件包 Google Authentication Library for Python、google-auth,版本 >= 1.21.3(google-auth v1.3.0 引入 Credentials.from_authorized_user_file,v1.8.0 引入 @ 987654330@ 和 v1.21.3 修复了后一个函数 wrt 其类的 expiry 成员),然后您可以将您的 credentials 对象保存到 JSON 文本文件并从中加载。

下面是执行此操作的代码:

import os, json, io

...

def json_file_name(
        api_name = 'youtube',
        api_version = 'v3'):
    return f'token_api_name_api_version.json'

def load_credentials(
        api_name = 'youtube',
        api_version = 'v3'):
    cred_file = json_file_name(
        api_name, api_version)

    if not os.path.exists(cred_file):
        return None

    from google.oauth2.credentials import Credentials
    return Credentials.from_authorized_user_file(cred_file)

def save_credentials(
        cred, api_name = 'youtube',
        api_version = 'v3'):
    cred_file = json_file_name(
        api_name, api_version)

    with io.open(cred_file, 'w', encoding = 'UTF-8') as json_file:
        json_file.write(cred.to_json())

...

【讨论】:

谢谢@stvar!在Youtube Analytics API...上,我在deepnote 笔记本中成功使用了您的第一个解决方案...我刷新了内核,它仍然有效。根据您的评论“您必须了解以下特性......”生成的 .pickle 文件是否会永久(或至少在出现其他随机技术问题之前一段时间)或可能由于在几小时或几天内到期,它无法工作。我的目标是每天刷新并将数据发送到数据库。我想我明天打开我的 deepnote 笔记本时会看看它是否仍然可以在没有登录的情况下工作。 @David Erickson:存储的凭据(JSON 格式的 pickle 格式)在某些情况下确实会过期,这意味着您必须再次运行 OAuth 流程。具体来说,一个此类数据包中包含的 刷新令牌 可能会过期。这是此问题的官方规范:Refresh token expiration。请注意,发布状态为 Testing 的应用的刷新令牌的生命周期仅为 7 天。 谢谢@stvar。这些是我设置的确切设置。 A Google Cloud Platform project with an OAuth consent screen configured for an external user type and a publishing status of "Testing" is issued a refresh token expiring in 7 days. Youtube Analytics API 要求您使用 OAuth 并且是我们当前获取收入数据的唯一地方,因此您不能使用服务帐户或 API 密钥。我希望它们的持续时间超过 7 天,但这是有用的信息。

以上是关于每次使用 YouTube Data API v3 时如何绕过输入验证码来授权我的代码的主要内容,如果未能解决你的问题,请参考以下文章

YouTube API v3 每次都要求授权

使用 YouTube Data API v3 确定 YouTube 频道的上传速率

如何使用新的 YouTube Data API (V3) 获取某个频道的上传视频列表?

如何使用新的 YouTube Data API (V3) 获取某个频道的上传视频列表?

Youtube Data API V3 - 使用google.youtube.videos.list()获取视频时出错

Youtube Data API V3 - 使用 google.youtube.videos.list() 获取视频时出错