跳到正文
DEV Community· Fast Data·· 2 小時前AI 評分58

如何批量取得 YouTube 頻道及播放清單的逐字稿(文字或 JSON),無需 YouTube Data API

How to get YouTube transcripts in bulk (whole channels and playlists) as text or JSON, without the YouTube Data API

AI 導讀

批量取得 YouTube 頻道或播放清單逐字稿,可用 Apify 上的 YouTube Transcript & Subtitles Scraper 一次處理;工具會列出影片、擷取字幕,並按影片返回 JSON。

正文

You want the transcripts of a whole YouTube channel: to feed a RAG pipeline, summarise talks with an LLM, or count how often a topic comes up. The obvious first stop is the official YouTube Data API. It won't get you there. This post covers why, what the free DIY route looks like, and how to do it in bulk with one API call.

Disclosure: I built the YouTube Transcript & Subtitles Scraper on Apify that the last part uses. The numbers below come from real runs of it.

Why the YouTube Data API doesn't work for this

The Data API has a captions resource, so it looks like the right tool. But the captions.download reference says the method needs OAuth with the youtube.force-ssl or youtubepartner scope, and the user has to have permission to edit the video. If you call it on someone else's video, you get a 403 forbidden error: "The permissions associated with the request are not sufficient to download the caption track." Each call also costs 200 quota units, out of a default budget of 10,000 units a day.

So captions.download works for your own videos. Most transcript projects need other people's videos, and for those it doesn't work at all.

The DIY route: youtube-transcript-api

The Python library youtube-transcript-api reads the same captions the YouTube web player shows. It needs no API key and no OAuth:

# pip install youtube-transcript-api
from youtube_transcript_api import YouTubeTranscriptApi

ytt = YouTubeTranscriptApi()
fetched = ytt.fetch("zjkBMFhNj_g", languages=["en"])
text = " ".join(s.text for s in fetched)
print(len(text.split()), "words")

Here are the pros and cons.

Pros:

  • It's free and open source, and it's good. My actor uses it under the hood.
  • It handles auto-generated vs. manual captions, language selection and YouTube's built-in translations.
  • For a few videos on your own machine, it's all you need.

Cons:

  • IP blocks. YouTube blocks many cloud IP ranges (AWS, GCP, Azure and others). Code that works on your laptop often fails on a server with RequestBlocked / IpBlocked. The library's README recommends rotating residential proxies to get around this, which means a proxy subscription and more code to maintain.
  • One video at a time. It takes video IDs. To get a whole channel or playlist, you first have to list the video IDs with something like yt-dlp or the Data API's playlistItems endpoint, which uses up quota.
  • Upkeep. Retries, rate limits, bookkeeping for videos without captions, and fixes whenever YouTube changes something are all on you.

If you need ten videos from your laptop, stop here. If you need 500 videos from a cron job on a cloud server, read on.

Bulk: whole channels and playlists in one call

The actor takes any mix of video URLs, Shorts, @channel URLs, playlist URLs and search queries. It lists the videos with yt-dlp (metadata only, nothing is downloaded), fetches the transcripts, and returns one JSON item per video. It starts with cheap datacenter proxies and retries blocked requests through residential proxies automatically.

With curl (the call waits for the run to finish and returns the dataset):

curl -X POST \
  "https://api.apify.com/v2/acts/fast_data_asap~youtube-transcript-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "startUrls": [
      "https://www.youtube.com/@aiDotEngineer/videos",
      "https://www.youtube.com/playlist?list=YOUR_PLAYLIST_ID"
    ],
    "maxVideosPerSource": 50,
    "languages": ["en"],
    "includeSegments": true,
    "chunkSizeChars": 2000
  }'

With Python (pip install apify-client):

from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("fast_data_asap/youtube-transcript-scraper").call(run_input={
    "startUrls": ["https://www.youtube.com/@aiDotEngineer/videos"],
    "maxVideosPerSource": 12,
    "languages": ["en"],
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["status"] == "ok":
        print(item["title"], item["wordCount"], item["isGenerated"])

Each item has these fields:

  • videoId, url, title, channel, durationSeconds
  • status: ok, transcripts_disabled, no_transcript_in_requested_languages, video_unavailable, age_restricted or failed
  • language, languageCode, isGenerated, isTranslated, availableTranscripts
  • text: the full transcript as one string
  • segments: [{start, duration, text}] with timestamps in seconds
  • chunks: optional [{start, end, text}] blocks for embeddings
  • wordCount

Real numbers. Andrej Karpathy's "[1hr Talk] Intro to Large Language Models" runs 59:49. It came back as 12,151 words in 1,704 timestamped segments, for $0.002. A run on the AI Engineer channel (maxVideosPerSource: 12) returned 12 transcripts, all status: ok, 58,654 words in total, for $0.024.

Pricing: $2 per 1,000 transcripts, dropping to $1.70 and then $1.50 per 1,000 on higher Apify plans. You aren't charged for videos without a transcript; they still show up in the output with their status. Apify's $5 of free monthly credit covers about 2,500 transcripts.

Tip: auto-captions misspell product names

Almost every transcript you'll get is auto-generated (isGenerated: true). All 12 AI Engineer talks above were. Speech recognition is bad with proper nouns, which quietly breaks keyword counts and search. In those 12 talks:

Spoken Transcribed as Wrong
Kimi "kimmy" 19 of 19
DeepSeek "deep seek" 7 of 14
Claude Code "cloud code" 3 of 7
ChatGPT "chat gpt" / "jgpt" 2 of 7
Anthropic "entropic" 1 of 15

(Small sample: n=12 talks from one channel, and Kimi and DeepSeek both come from a single workshop. The pattern is still real.)

Normalise the text before you count or embed it:

import re

ALIASES = {
    r"\bkimmy\b": "Kimi",
    r"\bdeep seek\b": "DeepSeek",
    r"\bcloud code\b": "Claude Code",
    r"\bchat gpt\b|\bjgpt\b": "ChatGPT",
    r"\bentropic\b": "Anthropic",
}

def fix_names(text: str) -> str:
    for pattern, canonical in ALIASES.items():
        text = re.sub(pattern, canonical, text, flags=re.IGNORECASE)
    return text

for item in items:  # items = the dataset items from the run above
    if item["status"] == "ok" and item["isGenerated"]:
        item["text"] = fix_names(item["text"])

Only apply rules like cloud code → Claude Code to isGenerated transcripts on a topic where that's what the speaker meant. In a talk about cloud infrastructure, "cloud code" may be correct. Build your own alias list from the names that matter in your niche.

Summary

  • Your own videos: the YouTube Data API's captions.download works with OAuth.
  • A handful of other people's videos, run locally: use youtube-transcript-api. It's free and solid.
  • Whole channels, playlists or searches, run on a server or schedule: use a hosted scraper that handles listing videos, proxies and retries. Here's mine, at $2 per 1,000 transcripts, with no charge for videos that have none.

Whichever route you take, check isGenerated and clean up proper nouns before you trust any counts. Transcripts belong to their creators, so make sure your use follows copyright law and YouTube's terms.

來源:DEV Community · dev.to