问答
几百个 YouTube 视频的字幕怎么批量下载?
简短回答
把视频 ID 列成清单,开几个并发逐个调用 YouTube 视频字幕接口;和 YouTube 打交道的是 okdata,你这边没有 IP 会被封。每份字幕 $0.004,1,000 个视频最多 $4.00,没有字幕的视频不收费。
一位做传播研究的老师手里有一张 800 个讲座视频的表格,想在周五前把字幕都放进一个文件。用 youtube-transcript-api 跑,几十个视频之后就报 429 Too Many Requests,大家加的重试逻辑又往往对着同一个 IP 继续猛发。这种规模的批量任务要准备好三样东西:一份干净的 ID 清单,一个懂得退让的客户端,以及断点续跑。
第一步:拿到 ID 清单
- 手里已经有链接。 取
v=后面的值,或者youtu.be/、/shorts/后面那一段。字幕接口要的是 11 位视频 ID。 - 手里只有一个主题。 YouTube 视频搜索每页返回约 20 个视频,带播放量和频道,凑 100 个候选花 $0.02。
- 手里有一个频道。 官方 Data API 对频道的上传列表调用
playlistItems.list,每页最多 50 个视频,只消耗 1 个配额单位,列出整个频道这样最省。
第二步:能续跑的脚本
import json, os, time, requests
from concurrent.futures import ThreadPoolExecutor
URL = "https://api.okdata.online/v1/youtube/videos/transcript/get"
KEY = os.environ["OKDATA_API_KEY"]
OUT = "transcripts.jsonl"
def fetch(vid):
while True:
r = requests.get(URL, params={"id": vid}, timeout=90, headers={
"Authorization": f"Bearer {KEY}", "Idempotency-Key": f"transcript-{vid}"})
body = r.json()
if body["code"] == "rate_limited":
time.sleep(body.get("retry_after_seconds") or 2)
continue
return vid, body
ids = [line.strip() for line in open("video_ids.txt") if line.strip()]
done = {json.loads(l)["video_id"] for l in open(OUT)} if os.path.exists(OUT) else set()
with ThreadPoolExecutor(max_workers=4) as pool, open(OUT, "a", encoding="utf-8") as out:
for vid, body in pool.map(fetch, [i for i in ids if i not in done]):
if body["code"] == "ok":
out.write(json.dumps(body["data"]["item"], ensure_ascii=False) + "\n")
else:
print(vid, body["code"]) # not_found 表示没有字幕,不扣费
中途崩了就再跑一遍,已经拿到的视频会跳过。带上 Idempotency-Key 之后,24 小时内重试一个其实已经成功的请求,会直接返回原结果,不会扣两次钱。
整批要花多少
| 批量 | 最多花费 |
|---|---|
| 100 个视频 | $0.40 |
| 800 个视频 | $3.20 |
| 1,000 个视频 | $4.00 |
没有字幕的视频和出错的请求都不收费,实际账单只会更低。新 key 自带 0.10 的额度,够先拿一小部分视频把脚本跑通。
限制
- 4 个并发是比较稳妥的起点。key 的突发上限写在
X-RateLimit-Limit响应头里,rate_limited不扣费。 - 不做语音转文字,没有字幕的视频要对你有权使用的音频跑 Whisper。
- 每次调用只返回一种语言,以响应里的
lang为准。
什么情况换个做法
量小、偶尔跑一次的,在家里的网络上用 youtube-transcript-api,每次调用之间停一停,通常能跑完。自己频道的视频,用官方 API 直接下载字幕文件。从搜索到摘要的完整流程,见视频转文字方案。
更新于