如果你在搭建视频自动化流水线——AI 生成脚本、TTS 配音、程序化合成视频、定时发布——你需要 YouTube Data API v3 来实现无人工干预的自动上传。
YouTube 上传 API 要求每个频道都通过 OAuth 2.0 授权,产生一个 refresh token。你的服务器存储这个 token,之后就能代表该频道 7×24 小时自动上传视频,不需要再打开浏览器。
这篇指南覆盖完整流程:创建 Google Cloud 项目、配置 OAuth、对多个频道授权、验证上传是否成功——所有容易踩的坑都会提前说明。
完成后你会拥有:
- 1 个 Google Cloud 项目(所有频道共用)
- 1 个 OAuth 客户端(Desktop 类型)
- 每个 YouTube 频道 1 个 refresh token,存入数据库
- 可从任意 Python 脚本或任务队列调用的上传流程
第一步:创建 Google Cloud 项目
- 进入 console.cloud.google.com
- 点击顶部项目下拉框 → New Project
- 项目名随意,如
video-pipeline或yt-uploader - 创建 → 等约 30 秒 → 从下拉框选中新项目
一个项目管理所有频道,不需要每个账号单独建项目。
第二步:启用 YouTube Data API v3
- 进入 APIs & Services → Library
- 搜索 YouTube Data API v3
- 点击 → Enable
- 可选:同时启用 YouTube Analytics API(后续拉取数据指标用)
第三步:配置 OAuth 授权页面
这一步是最多人卡住的地方,严格按步骤来:
- 进入 APIs & Services → OAuth consent screen
- 选择 External(除非你有 Google Workspace 组织账号,那可以选 Internal)
- 填写:
- App name:随便填,如
Video Uploader(用户不会看到) - User support email:你的邮箱
- Developer contact email:你的邮箱
- App name:随便填,如
- Save and Continue
添加 Scope 权限
- 点击 Add or Remove Scopes
- 添加以下三个 scope:
https://www.googleapis.com/auth/youtube.upload— 上传视频https://www.googleapis.com/auth/youtube— 管理频道(元数据、播放列表)https://www.googleapis.com/auth/youtube.readonly— 读取频道信息
- Save and Continue
添加测试用户
- 关键步骤:应用处于”Testing”状态时,只有明确列出的测试用户才能完成 OAuth 授权。你必须把矩阵里每个频道对应的 Google 账号邮箱都加进来。
- 点击 Add Users → 逐一输入 Google 账号邮箱
- Save and Continue → 返回 Dashboard
Testing 模式 vs Production 模式
应用默认处于 Testing 模式,有两点必须知道:
- Testing 模式下 refresh token 7 天后过期。也就是说你的自动化每周都会断掉,需要重新授权。
- 要彻底解决,需要提交 Production 验证。Google 审核需要数天到数周。通过后 refresh token 永不过期。
- MVP 阶段可以先不管,token 过期了重新跑授权脚本,一个账号 30 秒搞定。
第四步:创建 OAuth 客户端凭证
- 进入 APIs & Services → Credentials
- 点击 Create Credentials → OAuth client ID
- Application type 选 Desktop app ← 重要,不要选 “Web application”
- 名称随意,如
video-uploader - 点击 Create
- 下载 JSON 文件 → 保存为项目目录下的
client_secret.json
这个 JSON 文件所有频道共用。频道之间的区分在第五步授权时自动区分,不在这里。
为什么选 Desktop app 而不是 Web? Desktop 流程使用本地重定向(
localhost),不需要你搭一个 Web 服务器来接收 OAuth 回调。本地跑脚本,浏览器里点授权,拿到 token,结束。
第五步:对每个频道授权
每个 YouTube 账号各跑一次以下脚本。脚本会打开浏览器,用拥有该频道的 Google 账号登录,授权后返回 refresh token 供存储。
安装依赖
pip install google-api-python-client google-auth-oauthlib
授权脚本
"""
youtube_oauth.py — 对一个 YouTube 频道进行 API 上传授权。
每个频道运行一次,用拥有该频道的 Google 账号登录。
输出一个包含 refresh token 和频道信息的 JSON 文件。
"""
import json
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
SCOPES = [
"https://www.googleapis.com/auth/youtube.upload",
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/youtube.readonly",
]
def authorize():
flow = InstalledAppFlow.from_client_secrets_file(
"client_secret.json", SCOPES
)
# 打开浏览器窗口 — 登录并授权
credentials = flow.run_local_server(port=8090)
# 验证:拉取频道信息,确认授权成功
youtube = build("youtube", "v3", credentials=credentials)
response = youtube.channels().list(part="snippet", mine=True).execute()
if not response.get("items"):
print("错误:该 Google 账号没有 YouTube 频道。")
print("先在 https://www.youtube.com/create_channel 创建频道")
return
channel = response["items"][0]
channel_id = channel["id"]
channel_title = channel["snippet"]["title"]
print(f"\n✅ 授权成功:{channel_title} ({channel_id})")
token_data = {
"channel_id": channel_id,
"channel_title": channel_title,
"access_token": credentials.token,
"refresh_token": credentials.refresh_token,
"token_uri": credentials.token_uri,
"client_id": credentials.client_id,
"client_secret": credentials.client_secret,
"scopes": list(credentials.scopes),
}
filename = f"tokens_{channel_id}.json"
with open(filename, "w") as f:
json.dump(token_data, f, indent=2)
print(f"💾 Token 已保存到 {filename}")
print(f"🔑 Refresh token:{credentials.refresh_token[:20]}...")
print(f"\n将此 refresh token 存入数据库,用于后续自动上传。")
if __name__ == "__main__":
authorize()
每个频道重复一次: 跑脚本 → 用账号 #1 登录 → 保存 token。再跑 → 用账号 #2 登录 → 保存 token。每次运行生成一个独立的 tokens_{channel_id}.json。
第六步:验证上传是否正常
接入流水线之前,先确认每个频道都能实际上传。
生成测试视频
# 5 秒黑屏无声 — 仅用于测试上传流程
ffmpeg -f lavfi -i color=c=black:s=1080x1920:d=5 \
-f lavfi -i anullsrc \
-shortest -c:v libx264 -c:a aac \
test_video.mp4
测试上传脚本
"""
test_upload.py — 上传测试视频,验证 OAuth 是否正常。
用法:python test_upload.py tokens_UCxxxx.json test_video.mp4
以 PRIVATE 上传,不会公开出现。测试完在 YouTube Studio 删除即可。
"""
import sys
import json
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
def test_upload(token_file: str, video_file: str):
with open(token_file) as f:
tokens = json.load(f)
credentials = Credentials(
token=tokens["access_token"],
refresh_token=tokens["refresh_token"],
token_uri=tokens["token_uri"],
client_id=tokens["client_id"],
client_secret=tokens["client_secret"],
scopes=tokens["scopes"],
)
youtube = build("youtube", "v3", credentials=credentials)
body = {
"snippet": {
"title": "Test Upload — Delete Me",
"description": "自动化流水线测试,可以删除。",
"tags": ["test"],
"categoryId": "28", # 科学与技术
},
"status": {
"privacyStatus": "private", # 不公开
"selfDeclaredMadeForKids": False,
},
}
media = MediaFileUpload(video_file, chunksize=-1, resumable=True)
request = youtube.videos().insert(
part="snippet,status",
body=body,
media_body=media,
)
response = None
while response is None:
status, response = request.next_chunk()
if status:
print(f"上传中... {int(status.progress() * 100)}%")
video_id = response["id"]
print(f"\n✅ 上传成功!")
print(f"🎬 视频 ID:{video_id}")
print(f"🔗 URL:https://youtube.com/watch?v={video_id}")
print(f"\n(已以 PRIVATE 上传,测试完请在 YouTube Studio 删除)")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("用法:python test_upload.py <token_file.json> <video_file.mp4>")
sys.exit(1)
test_upload(sys.argv[1], sys.argv[2])
对每个频道各跑一次:
python test_upload.py tokens_UCxxxx.json test_video.mp4
看到”上传成功”,该频道就可以接入自动化流水线了。
在流水线中使用 Token
Token 存入数据库后,上传代码通过以下方式重建凭证并自动刷新:
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
def get_youtube_client(oauth_tokens: dict):
"""从存储的 token 构建已认证的 YouTube 客户端。"""
credentials = Credentials(
token=None, # 会自动用 refresh_token 刷新
refresh_token=oauth_tokens["refresh_token"],
token_uri=oauth_tokens.get("token_uri", "https://oauth2.googleapis.com/token"),
client_id=oauth_tokens["client_id"],
client_secret=oauth_tokens["client_secret"],
)
return build("youtube", "v3", credentials=credentials)
google-auth 库会自动处理 access token 刷新。只要 refresh token 有效,你就再也不需要打开浏览器。
配额与限制
| 指标 | 数值 |
|---|---|
| 每项目每日配额 | 10,000 单位 |
| 每次视频上传消耗 | 约 1,600 单位 |
| 每次元数据更新消耗 | 约 50 单位 |
| 安全的每日上传量 | 约 6 个视频 |
| 5 个账号 × 1 视频/天 | 约 8,000 单位 ✅ |
| 10 个账号 × 2 视频/天 | 约 32,000 单位 ❌ 需申请扩容 |
申请扩容:Google Cloud Console → APIs & Services → YouTube Data API v3 → Quotas → Edit Quotas → 填写理由。通常 1–3 个工作日审批通过。
所有频道的上传共享同一个项目配额,规模化时需提前规划。
常见报错排查
“Access blocked: This app’s request is invalid” → 第四步选了 “Web application” 而不是 “Desktop app”。删掉重建。
“The caller does not have permission” → 该 Google 账号没有加入第三步的测试用户列表。添加后重试。
“Token has been expired or revoked”
→ 应用还在 Testing 模式,7 天过去了。重新跑 youtube_oauth.py 授权。永久解决方案:提交 Production 验证。
“quotaExceeded” → 已达到每日 10,000 单位限额。太平洋时间午夜重置。申请配额扩容。
上传成功,但 YouTube Studio 显示”Processing failed”
→ 视频文件损坏或编码不支持。YouTube 要求 H.264 视频 + AAC 音频的 MP4 容器。用 ffprobe your_video.mp4 确认。
“The request metadata specifies an invalid video description” → 描述超过 5,000 字,或包含 YouTube 屏蔽的 URL。建议控制在 4,000 字以内。
“Daily Limit for Unauthenticated Use Exceeded”
→ 凭证没有正确传递。检查存储的 token 中 refresh_token 是否为 null。
安全注意事项
- 不要把
client_secret.json或 token 文件提交到 git。 两者都加入.gitignore。 - Refresh token 等同于密码。 有条件的话存入数据库时加密。
- 一个 OAuth 客户端管理所有频道完全没问题。 频道身份区分来自 refresh token,不是客户端。
- 撤销授权: 如果某个账号被盗,在 myaccount.google.com/permissions 撤销授权,对应 refresh token 立即失效。