问答
youtube-transcript-api 报 IP 被封,服务器上怎么拿 YouTube 字幕?
简短回答
别再让服务器自己的 IP 去请求 YouTube 字幕,改调 YouTube 视频字幕接口(/v1/youtube/videos/transcript/get),由 okdata 去取。每份字幕 $0.004,没有字幕的视频返回 not_found,不扣费,也不需要住宅代理。
这个坑几乎人人踩过:做了个视频摘要工具,本地跑得好好的,一部署到 AWS、Railway 或者 Colab,第一个请求就报 RequestBlocked 或 IpBlocked。YouTube 会拒绝大量云服务器 IP 段的字幕请求,库本身改变不了服务器在哪个机房。项目的 issue 里还有人买了轮换代理照样被封。
解决思路很直接:不要再从这台服务器的 IP 去问 YouTube。服务器只调用一个带 key 的 HTTPS 接口,字幕由接口返回。
用哪个接口
YouTube 视频字幕传 11 位的视频 ID(id),可选传优先语言(lang)。返回全文,以及带时间的分段,每段有 start、end(单位秒)和 text。
| 数量 | 调用次数 | 费用 |
|---|---|---|
| 1 个视频 | 1 | $0.004 |
| 100 个视频 | 100 | $0.40 |
| 1,000 个视频 | 1000 | $4.00 |
这是上限。没有字幕的视频返回 not_found,不收钱。
直接替换原来的 fetch()
import os, requests
def fetch_transcript(video_id, lang=None):
params = {"id": video_id, **({"lang": lang} if lang else {})}
body = requests.get(
"https://api.okdata.online/v1/youtube/videos/transcript/get",
headers={"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"},
params=params, timeout=60,
).json()
if body["code"] == "not_found":
return None # 视频没有字幕,没有扣费
if body["code"] != "ok":
raise RuntimeError(f"{body['code']}: {body['message']}")
return body["data"]["item"]
t = fetch_transcript("dQw4w9WgXcQ", lang="en")
print(t["lang"], len(t["segments"]), t["text"][:200])
如果下游代码按 youtube-transcript-api 的格式读数据,duration 用 end - start 算出来就行。
还要留意的地方
- 传 ID,别传链接。
https://www.youtube.com/watch?v=dQw4w9WgXcQ里要传的是dQw4w9WgXcQ。 - 短时间内并发太多,可能碰到 key 的限流。
rate_limited不扣费,按Retry-After等几秒再发。错误码见错误说明。 - 请求超时的话,带同一个
Idempotency-Key重试;已经完成的结果会原样返回,不会重复扣费。 - 只支持有字幕的公开视频,这里不做语音转文字。
哪些情况换条路更好
- 已经买了好用的住宅代理池,量又特别大,继续用 youtube-transcript-api 可能更便宜。
- 只要几十个视频,在家里的宽带上跑这个库,一般不会被封。
- 自己频道的视频,官方 YouTube Data API 可以直接下载字幕文件,因为你有编辑权限。
- 视频本身没有字幕,就只能对你有权下载的音频跑 Whisper 这类语音识别。
更多批量处理的做法见视频转文字方案。
更新于