下载一段公开视频并不难,难的是把它稳定地纳入归档流程:为什么选了“最佳画质”却没有声音?为什么视频下载完成,最后却停在合并阶段?为什么同一条命令在终端能运行,放进定时任务就找不到 ffmpeg?这些问题通常不在下载动作本身,而在格式选择、依赖路径和脚本运行环境之间。
yt-dlp 是开源的命令行音视频下载工具,适合保存自己发布的内容、归档获得授权的媒体,以及为后续转写、剪辑建立素材输入。它负责解析页面、选择媒体流并下载文件;ffmpeg 则承担音视频合并、音频提取和部分格式处理。要把它用于自动化归档,关键不是堆参数,而是明确下载策略、保留状态,并让失败可以被定位。
先准备一个可验证的运行环境
安装方式和站点支持可能随项目更新变化,部署时应以 yt-dlp 官方仓库的安装说明为准。本文不把搜索结果中的某个版本视为“当前最新版”;配置投入使用前,应记录本机版本,再用有权下载的样本验证。
如果使用 Python 环境,可以建立独立虚拟环境:
python -m venv .venv
Linux、macOS 激活:
source .venv/bin/activate
Windows PowerShell 激活:
..venvScriptsActivate.ps1
安装并检查:
python -m pip install -U "yt-dlp[default]"
python -m yt_dlp --version
ffmpeg -version
ffprobe -version
通过 pip 安装 yt-dlp,不等于安装了 ffmpeg 可执行程序。 ffmpeg 和 ffprobe 需要另外准备;不要把同名 Python 包当作这两个程序。
建议将二者放在同一个目录,并把该目录加入 PATH。如果不方便修改环境变量,可以显式指定位置:
python -m yt_dlp --ffmpeg-location "/opt/ffmpeg/bin" --version
这里的 --version 只用于检查 yt-dlp 是否能启动,不能验证合并链路。真正的验证需要下载一段短媒体,确认后处理也能完成。
以下命令中的 URL 均应替换为有权访问和下载的媒体地址。多行命令使用 Bash 的续行写法;在其他终端中,可以改为单行运行。
首次运行:先查看,再选择,最后下载
拿到一个地址后,先查看可用格式:
python -m yt_dlp --ignore-config --no-playlist -F "URL"
--ignore-config 可以排除已有配置文件的干扰;--no-playlist 用于避免带播放列表参数的单条视频链接意外触发整份列表下载。
格式列表中的“视频”和“音频”未必在同一个文件里。高画质资源经常采用分离媒体流,因此不能只看分辨率,还要注意某个格式是否为纯视频或纯音频。
最佳画质与音轨合并
常用的选择表达式是:
python -m yt_dlp --ignore-config --no-playlist
-f "bv*+ba/b"
--merge-output-format mkv
-P "./media"
-o "%(title)s [%(id)s].%(ext)s"
"URL"
其中:
bv*:选择最佳的、包含视频的格式,它可能已经带有音频。+ba:在需要时配合最佳音频流进行合并。/b:前面的选择不可用时,回退到同时包含音视频的单一格式。--merge-output-format mkv:需要合并时使用 MKV 容器。%(id)s:将媒体标识加入文件名,降低同名内容冲突的概率。
这里的“最佳”取决于可用格式和工具的排序规则,不等于“最适合所有播放器”。MKV、MP4 是容器,H.264、AV1、AAC 等是编码;换容器不会自动换编码。
另外,--merge-output-format 控制的是合并输出。若下载的是无需合并的单文件,它不保证把该文件统一改成 MKV。需要统一容器时,可另外评估 --remux-video mkv;重新封装仍受容器与流格式兼容性约束。
限制分辨率,避免无意义地占用空间
日常资料库未必需要保存最高分辨率:
python -m yt_dlp --ignore-config --no-playlist
-f "bv*[height<=1080]+ba/b[height<=1080]"
--merge-output-format mkv
"URL"
这个表达式在两个分支都限制高度不超过 1080。如果只限制前半段,却使用不受限的 /b 回退,就可能违背原本的存储预算。
高度未知或没有匹配格式时,这条命令可能失败。归档流程中,这往往比静默下载一个不符合要求的文件更容易管理。确实需要兜底时,应先查看 -F 输出,再明确放宽条件。
只需要音频时
python -m yt_dlp --ignore-config --no-playlist
-f "ba/b"
-x --audio-format mp3 --audio-quality 0
"URL"
音频提取和转换需要 ffmpeg、ffprobe。这里的 --audio-quality 0 是转换质量设置,不能把原本有损的音源恢复为无损。
如果目的是保存源音频,而不是适配只接受 MP3 的下游工具,可以先使用 -f "ba" 下载最佳可用音频,不必为了统一扩展名增加一次有损转码。
字幕归档:先确认语言,再决定旁挂还是嵌入
字幕是否存在、有哪些语言、是人工字幕还是自动生成字幕,都应先检查:
python -m yt_dlp --ignore-config --list-subs "URL"
不要假设所有站点都使用完全相同的中文语言代码。按实际列出的代码选择,通常比一次请求全部语言更可控。
例如,提取英语以及匹配 zh 系列代码的字幕:
python -m yt_dlp --ignore-config --no-playlist
--skip-download
--write-subs --write-auto-subs
--sub-langs "en,zh.*"
--sub-format "vtt/best"
-P "./subtitles"
-o "%(title)s [%(id)s].%(ext)s"
"URL"
--skip-download 跳过媒体下载,适合只补齐字幕。自动字幕适合作为检索和转写素材,但不应直接视为准确记录,尤其是人名、术语和多语言混说的内容。
如果下游只接受 SRT,可以增加:
--convert-subs srt
字幕转换同样涉及 ffmpeg,且可能丢失原字幕中的部分样式或定位信息。
归档时更建议优先保留旁挂字幕文件:方便全文检索、修订和重新处理。如果更看重单文件播放体验,可以在下载媒体时增加 --embed-subs,但要验证目标容器和播放器的实际支持情况。
把稳定参数写入配置,而不是反复复制命令
相比直接依赖不同系统的默认配置位置,为项目维护一个显式配置文件更容易复现。例如创建 archive.conf:
# 输出与归档状态
--paths ./media
--output "%(title)s [%(id)s].%(ext)s"
--download-archive ./state/downloaded.txt
--continue
# 格式策略
--format "bv*[height<=1080]+ba/b[height<=1080]"
--merge-output-format mkv
# 元数据与字幕
--write-info-json
--write-subs
--sub-langs "en,zh.*"
--sub-format "vtt/best"
# 保守的资源使用
--limit-rate 2M
--concurrent-fragments 1
--sleep-interval 5
--max-sleep-interval 15
--sleep-requests 1
--retries 3
--fragment-retries 3
准备目录,并先用单条链接测试:
mkdir -p media state
python -m yt_dlp --ignore-config
--config-locations "./archive.conf"
--no-playlist "URL"
这里的 --ignore-config 用于隔离其他自动加载的配置,显式指定的配置文件仍会被读取。脚本环境中,配置文件、输出目录和归档记录最好都使用绝对路径。
几个容易忽略的细节:
- 归档记录不是媒体备份。
--download-archive用于跳过已记录的条目;即使文件后来被删除,记录仍可能导致再次运行时跳过。 - 归档记录不适合不加区分地跨任务共用。 只提字幕、只存音频和保存完整视频,完成条件并不相同。
- 元数据也需要保护。 信息 JSON 可能包含源地址、描述和其他敏感信息,不应默认公开。
- 磁盘预算不只看成品。 分离音视频与合并输出在后处理期间可能同时存在,需要为临时文件预留空间。
限速不是“防封禁保证”,退避比增加并发更重要
限速参数解决的是资源使用问题,不能提供不受平台限制的保证:
| 参数 | 主要作用 | 不能替代什么 |
|---|---|---|
--limit-rate 2M | 限制下载传输速率 | 不等于限制全部页面或接口请求频率 |
--concurrent-fragments 1 | 控制分片下载并发 | 不等于限制外部启动的进程数 |
--sleep-interval 5 与 --max-sleep-interval 15 | 在下载之间加入随机等待 | 不会自动处理所有服务端限流情形 |
--sleep-requests 1 | 在解析过程的请求之间加入等待 | 不保证账号或地址不会受到限制 |
--retries 3 | 限制下载重试次数 | 不能修复权限、登录或格式选择错误 |
如果调度器同时启动十个进程,单个进程的保守配置仍可能被整体并发抵消。个人归档通常可以从单任务、低分片并发、有限重试开始。
出现 HTTP 429 或明确的服务端限流提示时,应停止当前批次、延长后续调度间隔,并检查访问授权和请求规模,而不是不断切换代理、刷新凭证或增加重试。403 则可能涉及权限、地址失效或站点解析变化,不能一概归因为“下载太快”。
浏览器登录状态也不是通用解法。只有在站点允许、账号确有访问权限时,才考虑使用相关认证能力;Cookie 应按密码级敏感信息管理,不进入代码仓库或公开日志。
用 Python 调用:显式环境、串行执行、失败即停
自动化脚本不宜拼接一整段 shell 命令。使用参数列表调用子进程,可以减少引号错误,也避免把外部输入当作 shell 语法执行。
下面的示例逐条处理 urls.txt,固定输出和状态位置,并在失败时停止,避免服务端已经拒绝请求后仍继续跑完整个列表:
from pathlib import Path
import subprocess
import sys
BASE = Path(__file__).resolve().parent
MEDIA = BASE / "media"
STATE = BASE / "state"
MEDIA.mkdir(exist_ok=True)
STATE.mkdir(exist_ok=True)
urls = [
line.strip()
for line in (BASE / "urls.txt").read_text(
encoding="utf-8"
).splitlines()
if line.strip() and not line.lstrip().startswith("#")
]
for url in urls:
if not url.startswith(("https://", "http://")):
raise ValueError("仅接受 HTTP 或 HTTPS 地址")
command = [
sys.executable, "-m", "yt_dlp",
"--ignore-config",
"--no-playlist",
"--paths", str(MEDIA),
"--output", "%(title)s [%(id)s].%(ext)s",
"--download-archive", str(STATE / "downloaded.txt"),
"--format", "bv*[height<=1080]+ba/b[height<=1080]",
"--merge-output-format", "mkv",
"--limit-rate", "2M",
"--concurrent-fragments", "1",
"--sleep-interval", "5",
"--max-sleep-interval", "15",
"--sleep-requests", "1",
"--retries", "3",
"--fragment-retries", "3",
url,
]
result = subprocess.run(command, cwd=BASE, check=False)
if result.returncode != 0:
raise SystemExit("下载任务失败,已停止后续处理,请检查日志")
使用 sys.executable 可以让子进程沿用运行脚本的 Python 环境。定时任务中还应指定这个虚拟环境的解释器绝对路径;需要时,再将 --ffmpeg-location 加入参数列表。
这个示例适用于本机受信任的地址清单,不应直接改造成接受任意用户地址的公开下载服务。后者还需要地址访问控制、网络隔离、磁盘配额和任务超时,否则可能引入内网访问及资源耗尽风险。
正式调度时,还要补齐两项机制:防止同一任务重叠运行的锁,以及带时间戳、受限访问且可轮转的日志。不要让多个进程无协调地写同一份归档记录。
按故障发生阶段排查,而不是盲目重装
| 现象 | 优先检查 | 处理方向 |
|---|---|---|
| 找不到 ffmpeg 或 ffprobe | 运行任务的账号、PATH、可执行权限 | 用绝对路径或 --ffmpeg-location 指定位置 |
| 下载完成但合并失败 | ffmpeg 日志、磁盘空间、目标容器 | 保留中间文件,先定位后处理错误 |
| 提示没有匹配格式 | -F 输出与筛选条件 | 检查高度、编码或扩展名限制是否过严 |
| 文件有视频却没有声音 | 是否只选了纯视频格式 | 使用音视频组合表达式并确认合并成功 |
| 下载成功但播放器无法播放 | 容器内的实际编码 | 换兼容播放器,或另行制定转码策略 |
| 字幕没有生成 | --list-subs 输出、字幕类型、语言代码 | 区分人工与自动字幕,不把无字幕当作下载故障 |
| 终端成功、定时任务失败 | 工作目录、解释器、环境变量、目录权限 | 使用绝对路径,核对任务账号 |
| 403、429 或解析失败 | 详细错误、权限、请求规模、工具版本 | 分类处理,不进入无限重试 |
诊断时可以增加 --verbose。但在分享日志前,应检查并脱敏地址参数、本地路径及认证相关信息。
站点解析逻辑会变化,更新 yt-dlp 有时是必要步骤,但不宜在生产定时任务启动时无条件升级。更稳妥的流程是:记录现用版本,在独立环境更新,用少量授权样本验证格式选择、字幕和后处理,再替换正式环境。
归档成功,不应只以“命令退出”为标准
对重要资料,至少抽查一次成品:音轨是否存在、字幕是否对应、时长是否合理、目标播放器能否打开。下载记录只能说明流程状态,不能替代内容验收和备份。
yt-dlp 的实际价值,是把零散的多媒体下载变成可复现的输入流程。可靠的配置应做到:格式选择有明确边界,ffmpeg 路径可验证,重复运行有状态,限流后会停止,日志足以解释失败。
最后,公开可播放不等于允许下载、长期保存或重新传播。个人归档也应遵守版权、平台条款和账号权限,不绕过 DRM、付费墙或其他访问控制。把任务限定在有权处理的内容范围内,往往也能让自动化流程更简单、更稳定。

评论列表 (1条):
加载更多评论 Loading...