问答

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 这类语音识别。

更多批量处理的做法见视频转文字方案。

更新于