跳到正文

目录

yt-dlp:命令行音视频下载工具使用与架构指南

yt-dlp:命令行音视频下载工具使用与架构指南

目标读者:需要批量下载视频、提取音频、嵌入字幕或写元数据的工程师;想在脚本、服务或流水线里调用下载能力的开发者。 核心问题:怎么装、怎么挑格式、怎么排反爬、报错时改哪里。架构部分为排查服务,不要求先读完。 事实边界:命令、选项与架构依据 yt-dlp/yt-dlp 仓库 README 整理;站点支持与反爬策略随网站变动,文中数字以标注日期为准。

需要从 YouTube、Bilibili、TikTok 等站点批量下载视频、提取音频、嵌入字幕或写元数据时,命令行工具比图形界面更可控、可脚本化、可复现。yt-dlp 是这一场景下维护最活跃的开源实现,内置 1700+ 个站点提取器,从 youtube-dl fork 而来并持续合并上游修复。

阅读导航

  • 想先上手:直接看「安装」和「最小示例」
  • 想挑格式:看「功能详解 → 格式选择」
  • 想排反爬:看「网络与反封锁」和「常见问题」
  • 想嵌入代码:看「Python API」
  • 报错后想定位:看「架构解析」和「常见问题」

学习目标

读完本文,你应该能:

  • 在 Linux / macOS / Windows 上装好 yt-dlp 与 ffmpeg,并验证安装
  • 用 -f 和 -S 按清晰度、编码、文件大小等维度挑格式,说清两者的区别
  • 处理字幕、元数据、缩略图、章节与播放列表的常见场景
  • 被反爬拦截时,按顺序尝试更新、伪装、代理与 Cookie
  • 用 Python API 在代码里取元数据、下载并挂进度回调
  • 根据报错位置(Extractor / Downloader / PostProcessor)判断该改什么

一条下载命令的完整路径

先看一次下载请求会经过哪些环节。后面排查时,报错出现在哪一段,答案基本就在那一段:

URL
 │
 ▼
Extractor        匹配 URL、向站点请求并解析视频信息
                 (站点改版常报 "Unable to extract ...")
 │
 ▼
Format Selector  按 -f / -S 规则筛出要下载的流
 │
 ▼
Downloader       按协议下载(HTTP / HLS / DASH,可并发分片)
                 (网络问题常报 HTTP 403 / 429)
 │
 ▼
PostProcessor    ffmpeg 合并音视频、嵌字幕、写元数据
                 (环境问题常报 ffmpeg not found)
 │
 ▼
输出文件

各阶段的报错与改法对应关系,在「架构解析」里展开。

项目概览

指标值
GitHubyt-dlp/yt-dlp
Stars / Forks194k / 16.9k(2026-09-30 实测)
语言Python(CPython 3.10+ / PyPy 3.11+)
许可证Unlicense
维护状态活跃,master 日常推送

yt-dlp 能做的事集中在这几类:

  • 内置 1700+ 个站点的视频提取器,每个站点对应一个 Extractor 类
  • 格式选择与多维度排序(-f 按格式 ID,-S 按分辨率/codec/文件大小等维度)
  • 字幕下载、嵌入与格式转换
  • 元数据写入与缩略图嵌入
  • 插件扩展(自定义 Extractor 和 PostProcessor)
  • 内置自动更新通道(stable / nightly / master)

相比原版 youtube-dl,yt-dlp 的改动重点在跟得上网站节奏:YouTube 的 n-sig 反混淆、Shorts、Clips,格式排序的粒度,分片并发,以及插件系统。这也是当前 youtube-dl 生态里更新最频繁的分支。


安装

二进制文件(推荐)

直接下载对应平台的二进制文件,无需安装 Python:

# Linux/macOS
sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
sudo chmod a+rx /usr/local/bin/yt-dlp

# Windows(Windows 10+ 自带 curl.exe)
curl.exe -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe -o yt-dlp.exe

# macOS 专用构建
sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos -o /usr/local/bin/yt-dlp
sudo chmod a+rx /usr/local/bin/yt-dlp

pip 安装

python -m pip install -U yt-dlp

通过 pip 安装后,升级稳定版用同一条命令加 -U 即可;想改用每日构建的 nightly 通道,加 --pre 安装预发布版本:

python -m pip install -U --pre yt-dlp

依赖:ffmpeg(必须)

yt-dlp 的多数能力依赖外部工具 ffmpeg,包括:

  • 合并分离的视频和音频流
  • 转换容器格式
  • 提取和嵌入字幕
  • 缩略图转换

之所以依赖外部 ffmpeg 而非内置,是因为 ffmpeg 本身体积大(数十 MB)、独立发布(GPL/LGPL 许可),打包进 yt-dlp 会让两者的分发和升级互相牵制;作为独立进程调用,各自升级互不影响。yt-dlp 只负责抓取和调度,编解码与容器操作全部交给 ffmpeg,两者通过子进程通信。

建议从 yt-dlp/FFmpeg-Builds 下载预编译版本。安装后运行 yt-dlp --version 确认安装成功。

⚠️ 注意:PyPI 上有个名为 ffmpeg 的 Python 包,和 ffmpeg 命令行工具不是同一个东西,别装错了。

更新通道

yt-dlp 提供三个发布通道:

通道特点适用场景
stable约每月发布一次一般用户
nightly每日构建,官方推荐日常用户使用多数用户首选
master每次 push 触发构建追求最新功能的开发者
# 更新到 nightly(推荐)
yt-dlp --update-to nightly

# 更新到 master
yt-dlp --update-to master

# 切换回 stable
yt-dlp --update-to stable

超过 90 天未更新的版本会提示更新警告,可用 --no-update 抑制。

由于视频站点经常调整反爬策略,yt-dlp 的 Extractor 需要持续跟进修复。遇到原本能下载的站点突然失败,第一步通常是更新到 nightly 再试。

验证安装

装完后确认两个程序都在 PATH 里:

yt-dlp --version
ffmpeg -version | head -n 1

预期输出分别是 2026.08.19 这类按日期命名的版本号(写本文时最新 stable 为 2026.08.19)和 ffmpeg version ...。yt-dlp --version 能跑通说明程序本身可用;ffmpeg 缺失不影响下载单文件流,但合并分离的音视频流、嵌入字幕这类操作会失败,建议一起装好。


最小示例

下载单个视频用一条命令:

yt-dlp "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

不指定格式时,yt-dlp 会按内置默认排序选最佳兼容格式。要显式指定质量最高的可用格式:

yt-dlp -f "bv*+ba/b" "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

bv* 匹配最佳视频流(含纯视频和音视频合一),ba 匹配最佳音频流,/b 是兜底选项。YouTube 等站点的高码率流通常是视频和音频分离的——这是 DASH(Dynamic Adaptive Streaming over HTTP)和 HLS 自适应流媒体的标准做法:分离后客户端可以根据带宽单独选择视频清晰度和音频码率,避免低带宽用户被迫下载高码率音频。代价是下载后需要 ffmpeg 把两条流合并成单个文件。

下载成功时终端会出现 [download] 100% of ... 的进度行,文件落在当前目录。若出现以 ERROR: 开头的行,说明本次下载中止,具体改法见「常见问题」。

功能详解

格式选择

格式选择是 yt-dlp 用起来和别的下载工具差别最大的地方。同一视频在不同站点、不同清晰度下往往有多个可用格式,-f 和 -S 提供两种互补的控制方式:-f 按格式 ID 或过滤表达式精确指定,-S 按多个维度排序后取最优。

列出可用格式

yt-dlp -F "https://www.youtube.com/watch?v=dQw4w9WgXcQ"

输出示例(截取部分,文件大小随时间变化):

ID      EXT   RESOLUTION FPS CH │   FILESIZE   TBR PROTO │ VCODEC          VBR ACODEC      ABR ASR MORE INFO
───────────────────────────────────────────────────────────────────────────────────────────────────────────
18      mp4   640x360     30    │        ~1.36MiB  388 https │ avc1.42001E         mp4a.40.2      360p
22      mp4   1280x720    30    │       ~3.29MiB  937 https │ avc1.64001F         mp4a.40.2      720p
137     mp4   1920x1080   30    │      ~25.79MiB 7354 https │ avc1.640028                        1080p
251     webm  audio only        │        ~2.66MiB   76 https │                     opus       55k

第一列 ID 是后续 -f 选择格式的依据。注意 18、22 这类小 ID 是音视频合一的渐进式格式,137 这类则是纯视频流,需要再配一条音频流。

选择特定格式

# 按格式 ID 选择
yt-dlp -f 18 "URL"

# 组合视频+音频
yt-dlp -f "18+251" "URL"

# 选择最佳视频,不带音频
yt-dlp -f "bv" "URL"

格式排序(Format Sorting)

yt-dlp 在 -S 参数上提供格式排序机制,按多个维度排序后取最优:

# 优先选 1080p 以内分辨率最高的,再偏好 h264 编码
yt-dlp -S "res:1080,codec:avc" "URL"

# 优先选择 av1 编码(vcodec 排序中 av01 本就靠前,可用 codec 指定偏好)
yt-dlp -S "codec:av01" "URL"

# 反转某个维度的排序方向(+ 表示升序,比如要最小的分辨率)
yt-dlp -S "+res" "URL"

res:1080 这类写法表示"优先更大的分辨率,但不超过 1080p";冒号后跟的是首选值,不是排序方向——排序方向用 + 前缀反转。字段值还可以做就近匹配,例如 filesize~1G 选最接近 1 GiB 的格式。

默认排序依次考虑 lang、quality、res、fps、hdr:12、vcodec、channels、acodec、size、br、asr、proto、ext、hasaud、source、id,其中 hasvid(有视频流优先)和 ie_pref(站点偏好)始终排在最前,用户指定的 -S 也压不过它们。想看某个视频的格式实际按什么顺序排出来的,用 yt-dlp -v -F "URL",verbose 日志里会打印排序依据。

-S 与 -f 的区别:-f 是硬过滤,不满足条件的格式直接排除;-S 是软排序,所有格式都参与,只是按指定维度排先后。需要严格限制清晰度时用 -f 加过滤表达式,需要在多个维度间权衡时用 -S。

格式过滤器

# 只选择不低于 720p 的视频
yt-dlp -f "bestvideo[height>=720]+bestaudio/best[height>=720]" "URL"

# 排除 webm 格式(用过滤器 [ext!=webm])
yt-dlp -f "bestvideo[ext!=webm]+bestaudio[ext!=webm]/best[ext!=webm]" "URL"

字幕处理

# 下载所有字幕
yt-dlp --write-subs "URL"

# 下载指定语言字幕
yt-dlp --write-subs --sub-langs "en,zh-cn" "URL"

# 嵌入字幕到视频文件
yt-dlp --embed-subs "URL"

# 下载自动生成的字幕并嵌入视频
yt-dlp --write-auto-subs --embed-subs "URL"

--write-subs 把字幕作为独立文件保存,--embed-subs 通过 ffmpeg 把字幕轨道写入容器(mp4/mkv)。两者可以同时使用:既保留独立字幕文件,又嵌入到视频里。自动生成的字幕要先写盘才能嵌入,所以嵌入自动字幕需要 --write-auto-subs 加 --embed-subs 组合。

元数据与缩略图

# 写入元数据 JSON
yt-dlp --write-info-json "URL"

# 嵌入缩略图
yt-dlp --embed-thumbnail "URL"

# 全部写入
yt-dlp --write-description --write-info-json --write-thumbnail --embed-thumbnail "URL"

--write-info-json 保存的视频元数据(标题、上传者、时长、格式列表等)可以后续被 --load-info-json 重新加载,避免重复请求站点 API。批量下载时先抓 info-json 再决定下载哪些格式,能减少对站点的重复请求。

下载时间范围与章节

# 只下载第 10 秒到第 60 秒
yt-dlp --download-sections "*10-60" "URL"

# 按章节分割视频
yt-dlp --split-chapters "URL"

--download-sections 按时间戳或章节只下载选定的部分;实际能省多少下载量取决于流类型与关键帧位置。--split-chapters 按视频自带的章节标记切成多个文件。

播放列表处理

# 只下载播放列表前 10 个视频
yt-dlp -I 1:10 "PLAYLIST_URL"

# 随机顺序下载
yt-dlp --playlist-random "PLAYLIST_URL"

# 排除已下载的视频(archive)
yt-dlp --download-archive archive.txt "PLAYLIST_URL"

--download-archive 把已下载视频的 ID 写入归档文件,下次运行时跳过。批量下载长播放列表时配合使用,中断后重跑不会重复下载。


输出模板与文件组织

下载大量视频时,文件命名和目录结构决定了后续检索的难易度。yt-dlp 的输出文件名通过 -o 模板完全可定制,模板字段来自 Extractor 返回的元数据。

基本模板

# 默认模板:标题 [ID].扩展名
# Example: "Rick Astley - Never Gonna Give You Up [dQw4w9WgXcQ].mp4"

# 自定义文件名
yt-dlp -o "%(title)s-%(id)s.%(ext)s" "URL"

# 按日期组织
yt-dlp -o "%(upload_date>%Y/%m/%d)s/%(title)s.%(ext)s" "URL"

%(upload_date>%Y/%m/%d)s 里的 > 是格式化操作符,把原始的 YYYYMMDD 字符串重新格式化为目录路径。

模板字段速查

字段说明
%(title)s视频标题
%(id)s视频 ID
%(uploader)s上传者
%(upload_date)s上传日期(YYYYMMDD)
%(resolution)s分辨率
%(ext)s扩展名
%(playlist_title)s播放列表标题
%(playlist_index)03d播放列表序号(补零 3 位)

多路径配置

# 指定下载路径
yt-dlp -P "/path/to/download" "URL"

# 临时文件先下载到 temp,完成后移到 home
yt-dlp -P "home:/data/videos" -P "temp:/tmp/yt-dlp" "URL"

-P 的 home: 和 temp: 前缀分别控制最终输出目录和临时文件目录。临时文件先落在 temp、处理完才移入 home,下载中断时目标目录不会留下残缺文件;把 temp 放到 SSD 也能加快分片写入。


网络与反封锁

视频站点识别非浏览器流量的常见手段包括 IP 地理限制、TLS 指纹检测、Cookie 验证和 UA 检查。yt-dlp 针对这些场景提供了对应的参数。

代理

# HTTP/HTTPS 代理
yt-dlp --proxy "http://proxy.example.com:8080"

# SOCKS5 代理
yt-dlp --proxy "socks5://user:[email protected]:1080"

伪装客户端(Impersonation)

部分网站通过 TLS 指纹识别机器人流量——即使 UA 和 Cookie 都对,TLS 握手的 cipher 列表和扩展顺序暴露了客户端不是真浏览器。yt-dlp 支持伪装成主流浏览器:

# 伪装成 Chrome
yt-dlp --impersonate "chrome" "URL"

# 伪装成 Windows 10 上的 Chrome(CLIENT[:OS] 语法)
yt-dlp --impersonate "chrome:windows-10" "URL"

# 查看所有支持的伪装目标
yt-dlp --list-impersonate-targets

底层使用 curl_cffi(curl-impersonate 的 Python 绑定)实现 TLS 指纹伪装,伪装目标覆盖 Chrome、Edge 和 Safari。这是可选依赖:pip 安装用 pip install "yt-dlp[default,curl-cffi]";官方预编译二进制大多数已内置,例外是 Unix 的 zipimport 版 yt-dlp 和 Windows 32 位版 yt-dlp_x86。

地理限制绕过

# 使用指定国家的 XFF 头
yt-dlp --xff "US" "URL"

# 使用代理验证 IP(针对某些需要二次验证的站点)
yt-dlp --geo-verification-proxy "http://proxy.example.com:8080" "URL"

--xff 通过添加 X-Forwarded-For 头模拟来自指定国家的请求,对部分只检查 HTTP 头的站点有效;对真正校验 IP 的站点仍需配合代理。


批量下载与自动化

批量文件

将 URL 写入文件,每行一个:

# urls.txt
https://www.youtube.com/watch?v=video1
https://www.youtube.com/watch?v=video2
https://www.youtube.com/watch?v=video3
yt-dlp -a urls.txt

-a 会顺序处理每个 URL。配合 --download-archive 可以跳过已下载项。yt-dlp 默认就是遇到下载错误继续处理下一个视频(--no-abort-on-error),想连后处理错误也一并忽略时加 -i(--ignore-errors)。

预设别名(Preset Aliases)

yt-dlp 内置几个常用组合,用 --preset-alias 调用:

# 提取音频并转成 mp3
yt-dlp --preset-alias mp3 "URL"

# 下载并转为 mp4 容器(格式偏好 h264/aac)
yt-dlp --preset-alias mp4 "URL"

内置预设还有 aac、mkv、sleep 等,官方承诺未来只增不改名。mp3 相当于 -f 'ba[acodec^=mp3]/ba/b' -x --audio-format mp3,mp4 相当于 --merge-output-format mp4 --remux-video mp4 -S vcodec:h264,lang,quality,res,fps,hdr:12,acodec:aac。

更常用的组合用 --alias <名称> "<选项串>" 自定义,通常写进配置文件 ~/.config/yt-dlp/config,之后直接以 --<名称> 调用:

# 配置文件里定义
--alias my-audio "-x --audio-format m4a -S aext:m4a,abr"

# 命令行里调用
yt-dlp --my-audio "URL"

适合在团队内部统一下载规格:把规格写进共享配置,成员各自用 --<名称> 调用即可。

日志与输出

# 安静模式(只打印进度)
yt-dlp -q "URL"

# 打印 JSON 信息
yt-dlp -j "URL"

# 打印特定字段
yt-dlp -O "%(title)s - %(resolution)s" "URL"

# 进度模板
yt-dlp --progress-template "[%(playlist_index)d/%(playlist_count)d] %(title)s" "URL"

-j 输出的 JSON 可以管道给 jq 做批量处理,是脚本化场景下提取元数据的标准做法。


Python API:嵌入到代码中

yt-dlp 本身就是 Python 包,命令行能力全部通过 yt_dlp.YoutubeDL 类暴露。需要在 Web 服务、定时任务或数据处理流水线里调用下载能力时,直接用 Python API 比起 shell 调用命令行更可控——可以拿到结构化元数据、注入进度回调、动态调整选项。

基础调用

from yt_dlp import YoutubeDL

URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc']
with YoutubeDL() as ydl:
    ydl.download(URLS)

with 语句确保下载完成后正确释放网络连接和文件句柄。

配置选项

import yt_dlp

ydl_opts = {
    'format': 'm4a/bestaudio/best',
    'postprocessors': [{
        'key': 'FFmpegExtractAudio',
        'preferredcodec': 'm4a',
    }],
    'outtmpl': '%(title)s.%(ext)s',
}

with YoutubeDL(ydl_opts) as ydl:
    ydl.download(['https://www.youtube.com/watch?v=BaW_jenozKc'])

ydl_opts 的字段与命令行参数一一对应,format 对应 -f,outtmpl 对应 -o,postprocessors 对应 --add-metadata、--embed-subs 等后处理开关。

获取元数据(不下载)

import yt_dlp

with YoutubeDL({'quiet': True}) as ydl:
    info = ydl.extract_info('https://www.youtube.com/watch?v=BaW_jenozKc', download=False)
    print(f"标题: {info['title']}")
    print(f"时长: {info['duration']}秒")
    print(f"格式数: {len(info['formats'])}")

download=False 只提取元数据不下载文件,返回的 info 字典结构与 -j 命令行输出的 JSON 一致。批量场景下可以先 extract_info 拿到时长和格式列表,过滤后再决定是否下载。

进度钩子

import yt_dlp

def progress_hook(d):
    if d['status'] == 'downloading':
        print(f"进度: {d.get('_percent_str', 'N/A')}")
    elif d['status'] == 'finished':
        print('下载完成,进入后处理...')

ydl_opts = {
    'progress_hooks': [progress_hook],
}

with YoutubeDL(ydl_opts) as ydl:
    ydl.download(['https://www.youtube.com/watch?v=BaW_jenozKc'])

progress_hooks 接收一个回调列表,下载过程中每个状态变化都会触发。d['status'] 的取值包括 downloading、finished、error,可用于实现自定义进度条、Webhook 通知或失败重试逻辑。


插件系统

yt-dlp 支持通过插件扩展功能,可加载自定义的 Extractor(提取器)和 PostProcessor(后处理器)。插件机制让自定义逻辑不必修改 yt-dlp 主仓库,便于在团队内分发和版本管理。

在插件系统出现之前,要支持一个新站点只能 fork 主仓库改代码再提 PR,等合并发版周期长,企业内部分站点的提取逻辑也不适合公开。插件系统把扩展点开放出来:自定义代码放在独立目录,yt-dlp 启动时自动扫描注册,主仓库升级不会覆盖插件,团队内部站点支持可以独立维护。

插件目录

在以下位置放置包含 yt_dlp_plugins 命名空间的包:

平台路径
Linux/macOS~/.config/yt-dlp/plugins/<pkg>/yt_dlp_plugins/
Windows%APPDATA%/yt-dlp/plugins/<pkg>/yt_dlp_plugins/
便携安装<yt-dlp.exe所在目录>/yt-dlp-plugins/<pkg>/yt_dlp_plugins/

插件示例

# mysitepkg/yt_dlp_plugins/extractor/mysite.py
from yt_dlp.extractor.common import InfoExtractor

class MySiteIE(InfoExtractor):
    _VALID_URL = r'https?://mysite\.com/watch/(?P<id>\w+)'
    _TESTS = [{
        'url': 'https://mysite.com/watch/abc123',
        'info_dict': {'id': 'abc123', 'title': 'My Video'},
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        # 自定义提取逻辑
        return self.url_result(f'https://mysite.com/api/video/{video_id}')

公开类名以 IE(后处理器以 PP)结尾才会被自动导入;类名或模块名以下划线开头会被当作私有跳过,__all__ 也可以控制导入范围。_VALID_URL 是匹配规则,_real_extract 是实际请求和解析逻辑,返回的字典结构与内置 Extractor 一致。要替换某个内置提取器,继承它并设置 plugin_name 类参数(如 class MyPluginIE(SomeBuiltinIE, plugin_name='myplugin'))。官方在 yt-dlp-sample-plugins 仓库提供了可直接套用的插件包模板。


架构解析

下载失败时,判断问题出在 Extractor、Downloader 还是 PostProcessor,决定了该改 URL 格式、网络参数还是 ffmpeg 选项。下面拆解 yt-dlp 的三层结构和一次下载请求的完整流程。

模块结构

yt_dlp/
├── YoutubeDL.py       # 主引擎:调度下载流程
├── extractor/         # 1700+ 网站提取器
│   ├── _extractors.py # 所有提取器的注册表
│   ├── youtube/       # YouTube 提取器(拆分为包:_base.py、_video.py 等)
│   └── ...
├── downloader/        # 下载器实现
│   ├── http.py        # HTTP 下载
│   ├── hls.py         # HLS 流下载
│   ├── dash.py        # DASH 流下载
│   └── fragment.py    # 分片并发下载
├── postprocessor/     # 后处理器
│   ├── ffmpeg.py      # ffmpeg 封装(合并/转码/字幕)
│   ├── embedthumbnail.py
│   └── sponsorblock.py
├── networking/        # 网络层(含 curl_cffi 伪装支持)
└── utils/             # 工具函数

extractor/ 目录是 yt-dlp 维护工作量最大的部分,每个站点对应一个 *.py 文件,注册表 _extractors.py 把所有 Extractor 类按字母序导入。站点改版或反爬升级时,通常改对应的那一个文件就够了。

下载流程

一次完整的下载请求流经以下阶段:

URL 输入 → Extractor.match_id()    # 匹配 URL,确定使用哪个提取器
        → Extractor._real_extract() # 向网站请求数据,解析视频信息
        → YoutubeDL.extract_info()  # 获取视频元数据(formats、字幕等)
        → Format Selector           # 根据用户选项筛选格式
        → Downloader                # 下载视频/音频流(HLS/DASH 分片边下边拼接)
        → PostProcessor             # ffmpeg 合并、转码、字幕嵌入、元数据写入
        → Output

报错位置决定了排查方向:Unable to extract 错误出在 Extractor 阶段,通常是站点改版需要更新 yt-dlp;HTTP Error 403/429 出在 Downloader 阶段,需要加 --impersonate 或换代理;ffmpeg not found 出在 PostProcessor 阶段,是 ffmpeg 未安装或不在 PATH。

Extractor 机制

每个支持的网站都有一个对应的 Extractor 类,主要职责:

  1. match_id():从 URL 中提取视频 ID
  2. _real_extract():向目标网站发起请求,解析页面/API,返回视频信息字典
  3. url_result():将提取结果转换为标准化格式

以 YouTube 为例,其 Extractor 还要处理 signature 与 n-sig(n 参数签名挑战)的解密。YouTube 会持续更新签名算法,yt-dlp 必须同步跟进,所以遇到 YouTube 下载失败时,第一反应先更新版本。

Downloader 分层

yt-dlp 支持多协议和多层并发:

下载层说明
HTTP Downloader基础 HTTP 下载,支持断点续传
HLS Downloader下载 .m3u8 清单文件,按分片下载(典型为 TS)
DASH Downloader下载 .mpd 清单文件,按 segment 下载
Fragment Downloader并发下载多个分片(-N 参数控制并发数)

默认 -N 1(单线程),对于 HLS/DASH 流可提升到 4-8 加快速度。并发数过高可能触发站点限速,需要根据目标站点的反爬策略调整。分片流的下载与拼接在 FragmentFD.download_and_append_fragments 里同时进行:分片按顺序下载、追加进输出文件,下载完成即得完整流,无需单独的合并步骤。

PostProcessor 链路

后处理在下载完成后串行执行,典型链路:

FFmpegMergerPP          # 合并视频+音频流(若有)
  → FFmpegFixupStretchedPP  # 修复拉伸的流
  → FFmpegFixupM4aPP      # 修复 M4a 元数据
  → FFmpegSubtitlesConvertorPP  # 字幕格式转换
  → FFmpegEmbedSubtitlePP   # 嵌入字幕到 mp4/mkv
  → FFmpegMetadataPP        # 写入元数据
  → EmbedThumbnailPP        # 嵌入缩略图
  → SponsorBlockPP          # 标记/移除赞助段落

每一步都是可插拔的 PostProcessor(源码在 postprocessor/ 目录,类名以 PP 结尾),通过 postprocessors 参数可以添加、移除或调整顺序。某一步失败会报错并可能让输出文件缺少对应的内容(如字幕没嵌入但视频正常),是否继续处理取决于容错设置。


常见问题

Q: 下载视频被限速或拒绝?

按顺序尝试以下组合,从轻到重:

# 1. 先更新到 nightly(站点反爬更新频繁)
yt-dlp --update-to nightly

# 2. 加浏览器伪装
yt-dlp --impersonate "chrome" "URL"

# 3. 配合代理
yt-dlp --impersonate "chrome" --geo-verification-proxy "http://proxy:port" "URL"

如果仍然失败,检查是否需要登录 Cookie:用 --cookies-from-browser chrome 直接读取浏览器 Cookie,或用 --cookies cookies.txt 加载导出的 Cookie 文件。

Q: 只想下载音频?

yt-dlp -x --audio-format mp3 "URL"

-x 触发 FFmpegExtractAudio 后处理器,--audio-format 指定输出格式,--audio-quality 0 用最高质量的 VBR 编码。想先选最高码率的音频流再转码,可以再指定排序 -S "abr"(音频码率降序优先)。

Q: PyInstaller 打包的 exe 启动太慢?

yt-dlp 启动时会导入所有 Extractor,1700+ 个类的导入开销在 PyInstaller 冻结环境下尤其明显。构建前先生成懒加载提取器:

python devscripts/make_lazy_extractors.py
python -m bundle.pyinstaller

这会把 Extractor 导入推迟到首次匹配 URL 时,官方文档确认此举能明显改善二进制的启动速度。

Q: 报错 “No video formats”?

通常是站点反爬机制触发,可尝试:

yt-dlp --no-check-certificates --impersonate "chrome" "URL"

--no-check-certificates 跳过 TLS 证书校验,对使用了自签证书或证书过期的内部站点有用;--impersonate 解决 TLS 指纹检测问题。

Q: 如何查看所有支持网站?

yt-dlp --list-extractors

输出所有已注册 Extractor 的名称;想看每个提取器的描述,改用 --extractor-descriptions。完整站点列表也可在 yt-dlp Supported Sites 查看。

Q: 文件名带特殊字符导致报错?

视频标题里的 /、:、| 等字符在文件系统中非法。yt-dlp 默认把它们替换为全角的相似字符(如 : 变 :、/ 变 ⧸),Windows 路径的目录段则替换为 #。需要更精细控制时加 --restrict-filenames,把文件名限制为 ASCII 字母数字加 -_.(README 示例中,标题 To'y!🤯😂🤦🏻‍♂️ 会变成 To_y);--windows-filenames 则强制按 Windows 命名规则兼容处理。

yt-dlp --restrict-filenames -o "%(title)s.%(ext)s" "URL"

Q: 批量下载到一半被站点限速?

先降低并发再加重试。-N 控制分片并发,--retries 控制重试次数,--sleep-requests 在请求间加随机延迟:

yt-dlp -N 2 --retries 10 --sleep-requests 1 -a urls.txt

配合 --download-archive 跳过已下载项,可以安全地多次重跑同一批 URL 直到全部完成。

与 youtube-dl 的主要差异

yt-dlp 相比原版 youtube-dl 的主要改进(来自官方 README):

差异yt-dlpyoutube-dl
Python 版本CPython 3.10+ / PyPy 3.11+2.6+/3.2+
格式排序默认按分辨率/codec默认按比特率
默认容错--no-abort-on-error中断
YouTube 支持n-sig 反混淆+Clips+Shorts基础
浏览器 Cookie支持所有主流浏览器有限
章节分割--split-chapters不支持
多线程下载--concurrent-fragments不支持
插件系统支持不支持

youtube-dl 的最后一次正式发布停留在 2021 年 12 月(2021.12.17),对新站点和 YouTube 反爬更新的跟进明显慢于 yt-dlp。新项目应直接选 yt-dlp;已有 youtube-dl 脚本可以通过 yt-dlp 命令别名平滑替换,多数参数兼容。

适用边界与采用建议

yt-dlp 适合:批量下载视频、提取音频、归档播放列表、嵌入到自动化流水线、需要精确控制格式和元数据的场景。

不适合:实时流媒体录制(用 streamlink 或 OBS)、需要 GUI 的非技术用户(社区有多个基于 yt-dlp 的图形前端)、对下载速度有极致要求且站点支持 aria2c 加速的场景(yt-dlp 的并发主要在分片层,不如 aria2c 的多连接下载)。

遇到站点无法下载时,按以下顺序排查:

  1. 更新到 nightly 版本(站点反爬更新频繁,旧版本可能已失效)
  2. 查阅 yt-dlp Issues 是否有相同站点的已知问题
  3. 用 -v 查看详细日志,定位失败发生在 Extractor、Downloader 还是 PostProcessor 阶段
  4. 站点不在支持列表时,考虑编写自定义 Extractor 插件

自测清单

能独立回答以下问题,说明本文内容基本消化:

  • 高码率视频为什么常下载视频、音频两条流再合并?合并依赖哪个外部工具?
  • -f "bv*+ba/b" 与 -S "res:1080" 分别是哪种控制方式,什么时候用哪个?
  • 下载被限速时,按什么顺序尝试更新、伪装、代理、Cookie?
  • --write-info-json 对批量下载有什么价值?--load-info-json 解决什么问题?
  • 报错 Unable to extract、HTTP Error 403、ffmpeg not found 分别对应哪一层?
  • 下载纯音频为 mp3、给视频嵌入字幕、按章节切片,分别用哪条命令?
  • 站点不在支持列表时,最小成本的支持方式是改主仓库还是写插件?

进阶路径

  • 固化常用选项:把 --impersonate chrome --embed-thumbnail --write-info-json 这类固定参数写进 ~/.config/yt-dlp/config,省去每次输入。
  • 增量归档脚本:用 Python API 配合 --download-archive,实现只补新视频的播放列表增量同步。
  • 写自定义 Extractor:对照「插件系统」的示例为内部站点写提取器,理解 _real_extract 返回的字段结构。
  • 读源码:从 extractor/youtube/ 包的 signature 解密与 n-sig 反混淆入手,理解反爬应对的边界。

参与讨论

使用 GitHub 登录。欢迎补充事实、异议与实践。