Библиотека — yt-dlp: извлечение субтитров и подготовка текста для LLM [X]
Шифр
yt-dlp
Разделы
tooling
Сверено
2 сентября — в этот день утверждения статьи сверялись с первоисточниками, а не правился текст
Объём
10 808 знаков · 11 разделов · чтения около 10 минут

yt-dlp: извлечение субтитров и подготовка текста для LLM


Утилита называется yt-dlp, а не yt-dl. Оригинальный youtube-dl фактически заброшен (последний значимый релиз — конец 2021) и регулярно ломается на актуальном YouTube из-за изменений в плеере. Активный форк — yt-dlp, drop-in замена с тем же синтаксисом плюс существенные расширения. Везде ниже имеется в виду он.

Архитектура: что вообще происходит при извлечении субтитров

yt-dlp состоит из набора extractor'ов (по одному на сайт) и слоя post-processor'ов. При запросе субтитров extractor парсит страницу/плеер YouTube, находит ссылки на дорожки субтитров и скачивает их в том формате, который отдаёт сам сайт. Для YouTube это VTT (WebVTT) или внутренний srv3/json3; формат srt YouTube напрямую не отдаёт — его yt-dlp получает конвертацией через ffmpeg (post-processor --convert-subs).

Из этого следуют два практических факта:

  1. --sub-format srt не гарантирует srt напрямую. yt-dlp скачает доступный формат (обычно vtt) и сконвертирует. Поэтому для конвертации нужен установленный ffmpeg.
  2. Различают два типа дорожек: загруженные автором (--write-subs, ключ subtitles) и автоматически сгенерированные распознаванием речи (--write-auto-subs, ключ automatic_captions). Это разные сущности — у большинства YouTube-роликов есть только вторые. Для полноты обычно запрашивают оба типа.

Базовые операции

Сначала всегда полезно посмотреть, какие дорожки вообще есть, не скачивая ничего:

yt-dlp --list-subs "https://www.youtube.com/watch?v=jNQXAC9IVRw"

Вывод разделён на Available subtitles (ручные) и Available automatic captions (автогенерация, обычно десятки языков, многие — машинный перевод).

Скачать субтитры без видео:

# ручные + авто, английский, в исходном формате (vtt), без видео
yt-dlp --skip-download --write-subs --write-auto-subs --sub-langs en \
  "https://www.youtube.com/watch?v=jNQXAC9IVRw"

Ключевые опции:

  • --skip-download — не качать сам медиафайл (для текста — всегда).
  • --write-subs / --write-auto-subs — ручные / автоматические дорожки.
  • --sub-langs en,ru — список языков. all — всё; en.* — все варианты английского (например en-US, en-orig); en-orig у YouTube означает оригинальную дорожку до перевода.
  • --sub-format vtt/srt/best — желаемый формат (для YouTube реально доступны vtt/srv*).
  • --convert-subs srt — пост-конвертация в srt/ass/lrc/vtt через ffmpeg.
  • -o / --output — шаблон имени файла (см. ниже).

Файл получит имя по шаблону %(title)s [%(id)s].%(ext)s, ext будет вида en.vtt.

Выбор языка: оригинал, а не перевод

Ключевое различие — между оригинальной дорожкой и машинным переводом. YouTube делает автоматическое распознавание речи (ASR) на языке оригинала, а любой другой язык в списке доступных автодорожек генерирует машинным переводом этой дорожки. То есть для англоязычного ролика --sub-langs ru даст текст, дважды прогнанный через автоматику: сначала ASR со своими ошибками, затем машинный перевод поверх этих ошибок. Качество деградирует на каждом шаге.

Отсюда правило: всегда бери дорожку на языке оригинала, а не перевод под удобный язык. Для LLM это вдвойне верно — модель свободно анализирует и английский, и русский на входе, а качество анализа определяется качеством исходного текста, а не его языком. Чистый оригинал лучше зашумлённого машинного перевода. Язык результата задаётся промптом к модели и от языка транскрипта не зависит.

Практически: для русскоязычного ролика оригинал — это ru, для англоязычного — en; машинный перевод (ru поверх английского ролика) не брать никогда. YouTube помечает оригинальную автодорожку суффиксом -orig, поэтому при неизвестном заранее языке можно перечислить приоритеты — yt-dlp возьмёт первый доступный из списка:

yt-dlp --skip-download --write-auto-subs --sub-langs "en-orig,en,ru-orig,ru" \
  --sub-format json3 -o "%(id)s.%(ext)s" "URL"

Это работает, но хрупко на разноязычных каналах: порядок приоритетов фиксирован. Для надёжного выбора оригинала язык определяют программно по метаданным (поле automatic_captions, дорожка с суффиксом -orig) — но это уже задача твоего скрипта.

Отдельный приоритет — субтитры, залитые автором (subtitles, не automatic_captions). Если они есть, бери их раньше любой автогенерации, включая оригинальный ASR: они вычитаны человеком и качественнее. Запрос обоих типов (--write-subs --write-auto-subs) даёт yt-dlp выбрать ручные, когда они доступны.

Получение чистого текста без тайм-кодов

Это твой основной сценарий, и здесь есть подводный камень. Сырой VTT от YouTube непригоден для прямого grep-удаления тайм-кодов. Автосубтитры YouTube используют эффект "roll-up": каждая реплика дублируется в нескольких соседних cue, а внутри строк стоят инлайн-теги тайминга по словам (<00:00:01.234><c> word</c>). Наивная фильтрация тайм-кодов даст текст с массой повторов.

Вариант 1 — рекомендуемый: srv3/json3 как источник

YouTube отдаёт формат json3 (он же srv3), где текст структурирован и не содержит roll-up-дублей. Самый чистый путь — скачать именно его и собрать текст собственной постобработкой:

yt-dlp --skip-download --write-auto-subs --sub-langs en --sub-format json3 \
  -o "transcript" "https://www.youtube.com/watch?v=jNQXAC9IVRw"

Структура файла: объект с массивом events, в каждом событии — массив segs, где полезный текст лежит в поле utf8. Чтобы получить связный текст, достаточно пройти по events[].segs[].utf8, отбросить пустые сегменты и склеить с нормализацией пробелов и переносов. Никакой временнóй разметки в результате не остаётся — это и есть оптимальный вход для LLM. (Сам разбор делается отдельным скриптом — здесь он не приводится.)

Вариант 2 — простой: конвертация в srt + чистка

Если json3 по какой-то причине недоступен, работает связка --convert-subs srt плюс постобработка. yt-dlp при конвертации vtt→srt уже схлопывает roll-up-дубли, поэтому из srt текст вычищается надёжно:

yt-dlp --skip-download --write-auto-subs --sub-langs en --convert-subs srt \
  -o "transcript" "https://www.youtube.com/watch?v=jNQXAC9IVRw"

Получится transcript.en.srt вида:

1
00:00:00,000 --> 00:00:02,500
Hello and welcome to the channel

Чистка в plain text одной командой (удаляем номера, тайм-коды и пустые строки, склеиваем):

sed -E '/^[0-9]+$/d; /-->/d; /^[[:space:]]*$/d' transcript.en.srt \
  | tr '\n' ' ' | sed -E 's/[[:space:]]+/ /g' > transcript.txt

Разбор фильтров sed: /^[0-9]+$/d убирает строки-номера cue, /-->/d — строки тайм-кодов, /^[[:space:]]*$/d — пустые. Затем tr склеивает переносы в пробелы и финальный sed схлопывает кратные пробелы.

Вариант 3 — встроенный downloader без ffmpeg

Если ffmpeg ставить не хочешь, у автодорожек YouTube часто доступен формат vtt, но для чистого текста его всё равно надо обработать. Тогда проще json3 (вариант 1) — ffmpeg для него не нужен, обработка чисто текстовая.

Вывод по форматам для LLM: бери json3 (вариант 1) — это самый устойчивый к дублям путь и без зависимости от ffmpeg. srt-связка (вариант 2) — хорошая запасная, когда хочется одной строкой и ffmpeg уже есть. Чистого --sub-format txt, который бы сразу давал готовый текст без тайм-кодов, у yt-dlp нет — постобработка неизбежна.

Пакетная обработка канала

Для твоей задачи (LLM по содержимому каналов) обычно нужен не один ролик, а весь канал или плейлист. yt-dlp принимает URL канала/плейлиста напрямую и обходит все видео.

yt-dlp --skip-download --write-auto-subs --sub-langs en --sub-format json3 \
  -o "%(channel)s/%(upload_date)s_%(id)s.%(ext)s" \
  "https://www.youtube.com/@channelname/videos"

Полезное при массовом обходе:

  • --download-archive archive.txt — ведёт журнал уже обработанных id; повторный запуск скачивает только новые видео. Это база для инкрементального обновления.
  • --sleep-requests 1 и --min-sleep-interval/--max-sleep-interval — паузы между запросами, чтобы не словить временную блокировку (HTTP 429) при обходе сотен видео.
  • --match-filter "duration > 60" — отсев, например, Shorts.
  • -I 1:50 (--playlist-items) — срез по диапазону.
  • --lazy-playlist — начинать обход, не дожидаясь полного разбора плейлиста.

Только метаданные без скачивания чего-либо (заголовки, описания, длительности — пригодится для индексации перед тем как качать субтитры):

yt-dlp --skip-download --print "%(id)s\t%(upload_date)s\t%(title)s" \
  "https://www.youtube.com/@channelname/videos" > index.tsv

Эксплуатационные нюансы

  • Свежесть версии критична. YouTube периодически меняет формат плеера, и устаревший yt-dlp отваливается с ошибками извлечения. Самообновляющаяся установка: yt-dlp -U. Установка из pip: pip install -U yt-dlp. Релизы выходят примерно раз в месяц (последний на сентябрь 2026 — 2026.08.19), а рядом живёт nightly-ветка с более частыми фиксами extractor'ов.
  • Безопасность. За 2026 год закрыты две инъекции произвольных команд: CVE-2026-26331 через --netrc-cmd (исправлено в 2026.02.21) и CVE-2026-55404 через недостаточную санитизацию вывода --write-link (исправлено в 2026.08.19). Ещё одна причина не сидеть на старой версии.
  • Блокировки и "Sign in to confirm you're not a bot". При активном обходе YouTube может требовать аутентификацию. Решается передачей cookies: --cookies-from-browser firefox или --cookies cookies.txt. Не злоупотребляй частотой запросов с залогиненного аккаунта.
  • ffmpeg как зависимость. Нужен для --convert-subs и любой пост-конвертации. Для пути через json3 не требуется.
  • Кодировки. json3 и vtt от YouTube — UTF-8. При последующей обработке в Windows-окружении явно работай в UTF-8, иначе кириллица и спецсимволы поедут.

Итог

Для подготовки текста под LLM оптимальный путь — скачивать субтитры в формате json3 и собирать текст из структуры events[].segs[].utf8 собственной постобработкой, получая связный текст без тайм-кодов и без roll-up-дублей, без зависимости от ffmpeg. srt-связка с чисткой через sed — надёжная запасная при наличии ffmpeg. По языку: всегда бери оригинальную дорожку (для русскоязычных каналов ru, для англоязычных en), приоритет ручным субтитрам автора над автогенерацией, и никогда — машинный перевод; язык ответа задаётся промптом к модели. Для каналов обязательно --download-archive для инкрементальности и троттлинг запросов против блокировок.


Связанные заметки

Из библиотеки института уносят только знания. Читателей здесь не считают: посещение не регистрируется, отметок в издании не остаётся, счётчиков нет ни у одного издания фонда.

Издание пополняется и пересверяется. Текст доступен на условиях CC BY-SA 4.0 — унесённое можно передавать дальше на тех же условиях.