1. 先想清楚:三条命令到底在跟谁说话
先把话说在前头:标题里提到的那个短视频平台,下文统一用“该平台”来指代,链接也一律写成https://<平台域名>/user/<用户ID>这种占位形式,你自己替换成实际地址就行。这篇内容讲的是用一套命令行工具链,把主页、合集、追更这三类内容稳定地落到本地磁盘,核心命令加起来确实只有三条,但三条命令背后的准备工作、参数取舍和容错设计,才是真正决定这套流程能不能长期跑下去的。适合谁看?手里有几十上百条内容要归档的人、做内容备份的创作者本人、需要离线整理素材的人,以及单纯想搞清楚“为什么别人三行命令就搞定,我跑一次报错一次”的动手派。
我最早干这活是手动一条条另存,后来写了个半自动脚本,再后来才收敛到现在的形态:一个工具、一个归档文件、三个场景各一条命令。中间踩过的坑比下载下来的视频本身还多——尤其是那种“第一次跑得好好的,第二次跑就重下几百条”的蠢问题,浪费的不只是流量,还有你对这套流程的信任。所以这篇不会只给你命令,会连带把目录结构、命名模板、增量判定的原理一起讲透,你照着抄能跑,改着用也不容易翻车。
有一点必须先说明白:这套流程只适用于你自己账号发布的内容、明确授权给你使用的内容,以及平台自身允许保存的公开内容,用于个人学习、备份和离线整理。任何把它用来批量搬运他人作品再对外分发、商用或者规避平台正常机制的做法,都不在本文讨论范围内,也不建议尝试。工具是中性工具,边界得自己守。
2. 环境准备:十分钟搭出可复现的下载环境
2.1 工具链选型:为什么是它而不是网页解析站
市面上做这类事的方案大致三类:在线解析网站、图形化下载软件、命令行工具。前两个上手快,但问题很明显——不可批量、不可增量、不可复现。你今天解析一条,明天解析一条,攒到五十条的时候想回头补,就得从第一页翻起;更麻烦的是在线站点经常会塞广告、限制并发,解析失败了你连日志都看不到,只能干瞪眼。
命令行工具的优势就在这里:它是幂等的。同一条命令跑十次,结果应该是一样的——已经下过的跳过,没下到的补上,失败的留下可读的错误信息。这一点对“追更”场景是决定性的,因为追更的本质就是“反复执行同一条命令,只处理新增部分”,如果工具本身不具备幂等性,这事儿根本没法自动化。
我用的核心工具是一个开源的命令行下载器(社区里通常叫它 yt-dlp,是早期某个同类项目的活跃分支),配套还需要ffmpeg做音视频合并。这两个东西的分工很明确:下载器负责解析页面、拿到真实的媒体流地址、按模板落盘;ffmpeg 负责把分离的视频轨和音频轨合成一个能直接播放的 mp4。很多人第一次跑完发现下来的是两个文件(一个无声视频、一个纯音频),就是缺了 ffmpeg 或者没配好路径。
安装这块,三个主流系统各一行:
# macOS(Homebrew) brew install yt-dlp ffmpeg # Windows(winget,PowerShell 里跑) winget install yt-dlp.yt-dlp winget install Gyan.FFmpeg # Linux(Debian 系,用 pipx 隔离环境更干净) sudo apt install ffmpeg pipx install yt-dlp装完先验一下版本,这一步别省:
yt-dlp --version ffmpeg -version | head -n 1提示:这个工具迭代非常快,平台侧一改页面结构,往往两三天内就会发新版适配。我习惯每月初顺手升级一次,比出问题再临时抱佛脚省事得多。
2.2 目录结构设计:先想好文件放哪儿
绝大多数人第一次用都是直接在桌面跑,结果一个月后桌面上躺着七百个文件名奇怪的视频,想找某一条得靠记忆。目录结构这件事,属于“花十分钟省十小时”的典型。
我现在的结构是按账号隔离、按类型分桶、归档文件单独存:
vault/ ├── user_<用户ID>/ │ ├── posts/ # 主页发布的散装内容 │ ├── collections/ # 合集 │ │ └── <合集标题>/ │ └── meta/ │ ├── archive_posts.txt # 主页增量归档 │ ├── archive_col.txt # 合集增量归档 │ └── failed.log # 失败清单 └── user_<另一个ID>/对应的输出模板参数长这样:
-o "vault/user_%(uploader_id)s/posts/%(upload_date>%Y-%m-%d)s_%(id)s_%(title).60s.%(ext)s"这里有几个细节值得解释。%(title).60s是截断到六十个字符,因为有些标题能写到一百多字,文件名过长在部分系统上会直接报错;%(upload_date>%Y-%m-%d)s是把上传日期格式化成2024-05-17这种可排序的写法,这样目录里按文件名排序就是按时间排序,比看时间戳直观;%(id)s放在标题前面是有意为之——标题可能重复、可能被改,但内容 ID 是唯一的,它是你日后去重的最后一道保险。
2.3 登录态处置:只用自己的账号,且只在本机
该平台的主页和合集在未登录状态下,能拿到的条目数量往往是残缺的,往下翻几屏就断了。所以这一步绕不开登录态。做法有两种:一是让工具直接读本机浏览器的 cookie,二是手动导出 cookie 文件。
第一种最省事:
--cookies-from-browser chrome它会去读本机 Chrome 的 cookie 库。注意这里有两个前提:你得在那个浏览器里用自己的账号正常登录过,而且浏览器在读取时最好是关闭状态,否则某些系统上数据库文件被占用会读失败。
第二种更可控,适合放到常驻脚本里:
--cookies cookies.txt导出后把文件权限收紧到只有自己能读:
chmod 600 cookies.txt注意:这个文件等价于你的登录凭证,绝对不能提交到任何代码仓库、不能传到网盘、不能随手扔在共享目录里。我见过有人在公开仓库里传了这个文件,后果自己想想。另外,登录态是会过期的,追更脚本跑着跑着突然大量失败,第一件事就是回头检查 cookie 是不是失效了,而不是去怀疑参数。
3. 三条命令逐条拆解:主页、合集、追更
3.1 命令一:主页全量,先探测再下载
主页是最容易被低估的场景。很多人上来就直接下,结果跑了两小时发现只拿到了最近二十条——因为列表是懒加载的,工具默认只解析第一页。正确姿势是分两步:先探测,确认能拿到多少,再决定怎么下。
探测命令:
yt-dlp --flat-playlist --print "%(id)s" \ "https://<平台域名>/user/<用户ID>" > list_posts.txt--flat-playlist是关键,它让工具只解析列表页、不进入每一条的详情页,速度快一个数量级。跑完wc -l list_posts.txt看一下条目数,心里就有底了。如果这个数字明显比你在网页上看到的总数少,说明登录态没生效或者列表翻页被截断,先解决这个问题再往下走。
确认数量没问题,正式下载:
yt-dlp --cookies cookies.txt \ -f "bv*+ba/b" --merge-output-format mp4 \ -o "vault/user_%(uploader_id)s/posts/%(upload_date>%Y-%m-%d)s_%(id)s_%(title).60s.%(ext)s" \ --download-archive vault/user_<用户ID>/meta/archive_posts.txt \ --write-info-json --write-thumbnail \ -N 4 --retries 10 --fragment-retries 10 \ "https://<平台域名>/user/<用户ID>"这条命令信息量比较大,逐段说。-f "bv*+ba/b"是格式选择表达式,意思是“优先最好的视频轨加最好的音频轨,实在不行就退而求其次拿单个合并好的文件”,bv*里的星号表示允许回退到次优;--merge-output-format mp4是让 ffmpeg 合并时统一封成 mp4,避免下来一堆 mkv 还得转。--write-info-json把元数据单独存一份,这东西日后做检索、做迁移都很有用,反正就几 KB。--write-thumbnail是顺手把封面存下来,做素材整理的时候能省二次抓取的麻烦。-N 4是并发分片数,后面调优章节会细讲。--retries 10 --fragment-retries 10是重试次数,网络抖一下不至于整条失败。
3.2 命令二:合集定向,只取你要的那一段
合集和主页在工具眼里其实是同一种东西——都是“列表”。区别只在链接形态和你的意图:主页是“这个账号发的全部”,合集是“这个主题下的全部”。所以命令骨架几乎一样,重点是加两个参数来精确控制范围。
yt-dlp --cookies cookies.txt \ -f "bv*+ba/b" --merge-output-format mp4 \ -o "vault/user_%(uploader_id)s/collections/%(playlist_title).40s/%(playlist_index)03d_%(id)s_%(title).50s.%(ext)s" \ --download-archive vault/user_<用户ID>/meta/archive_col.txt \ -I 1:40 \ -N 4 \ "https://<平台域名>/collection/<合集ID>"这里新出现两个东西。%(playlist_title).40s和%(playlist_index)03d是合集场景专用的模板变量:前者拿合集名字做子目录,后者拿条目在合集里的序号做前缀,03d是补零到三位,这样001、002一直到100在文件管理器里排序才不会乱。如果你不做补零,10会排在2前面,一次几十条看着就烦。
-I 1:40是条目范围限定,1:40表示第一到第四十条,也支持1,3,5(挑着下)和-I 1:10,20:30(多段)。这个参数在两种情况下特别救急:一是合集有几百条你只想要前面一部分,二是上次下到第 40 条中断了,这次直接从41:接着来——当然,有归档文件的话其实不需要手动指定,这里只是给你留个手动兜底的手段。
3.3 命令三:追更,靠归档文件实现“只下新的”
这是三条命令里最有价值的一条,也是最多人写错的一条。追更的核心不是“定时跑”,而是判断哪些是新的。如果判断逻辑错了,定时跑就是定时重下。
工具自带的--download-archive参数就是干这个的:它维护一个纯文本文件,每成功下载一条就追加一行内容 ID。之后再跑,遇到 ID 已经在这个文件里的,直接跳过。所以追更命令其实就是在主页命令上加了这个参数:
yt-dlp --cookies cookies.txt \ -f "bv*+ba/b" --merge-output-format mp4 \ -o "vault/user_%(uploader_id)s/posts/%(upload_date>%Y-%m-%d)s_%(id)s_%(title).60s.%(ext)s" \ --download-archive vault/user_<用户ID>/meta/archive_posts.txt \ --break-on-existing \ --dateafter today-14days \ --sleep-requests 1.5 \ -N 2 \ "https://<平台域名>/user/<用户ID>"--break-on-existing是个容易被忽略但极其重要的参数。它的作用是:遍历列表时一旦碰到已经归档过的条目,就停止继续往后翻。这利用了绝大多数平台列表页“新的在前”的特性——既然已经撞上旧的,后面的全是旧的,没必要再翻。实测下来,一个两百条内容的账号,追更一次的耗时从“翻完整个列表”压缩到“只翻到最新那几条”,差距是几十倍。
但这个参数有个副作用必须提醒:如果你的账号内容不是发布顺序排列(比如置顶内容、被重新编辑过的内容会跳到前面),--break-on-existing可能会提前刹车,导致中间的漏掉。我一般会留一手——每周跑一次带--dateafter today-30days的完整扫描,不带--break-on-existing,当作校验补漏。--sleep-requests 1.5则是每次请求之间歇一秒半,这个不是矫情,是为了别把人家服务器打得太难看,长期跑的东西,稳比快重要。
如果要把追更做成定时任务,Linux/macOS 下用 crontab 就行:
# 每天早上七点跑一次追更 0 7 * * * cd /path/to/vault && /usr/local/bin/yt-dlp -a /path/to/chase.conf >> /path/to/chase.log 2>&1把长命令写进chase.conf文件里,一行一个参数,比塞在 crontab 里可读性好太多。日志一定要留,出问题的时候那是唯一线索。
4. 参数调优:画质、速度、体积的三方平衡
4.1 并发与限速:-N到底开多大
-N是并发分片数,这个参数不是越大越好。它的原理是把一个视频切成若干片段并行下载,理论上并发数越高越快,但实际受限于三个因素:你的出口带宽、对方服务器的响应策略、以及本地磁盘的写入速度。
我的经验区间是:家用宽带 100M 以下开-N 2,200M 到 500M 开-N 4,千兆以上开-N 8也就到顶了。再往上收益急剧递减,反而更容易触发对方的限流策略,表现为大量分片超时、然后整条重试,实际速度还不如-N 2。这个参数我踩过坑:有次图快开了-N 16,结果一整批下来三分之一的文件不完整,还得全删重来。
配合并发建议加上限速,避免把家里的网全吃满导致其他设备卡死:
--limit-rate 8M -N 4这里的8M是 8 MB/s,四条并发合计差不多这个量级,留出余量给浏览器和会议软件。
4.2 画质选择:-S比-f更好用
早期版本调画质要写一长串-f表达式,现在有个更直观的参数-S(排序),按优先级写就行:
-S "res:1080,ext:mp4:m4a,codec:h264"这行的意思是:优先 1080p,优先 mp4 容器和 aac 音频,优先 h264 编码。为什么点名 h264?因为很多平台默认给的是更高压缩率的编码格式,体积小但老设备、老播放器、剪辑软件兼容性差,你导进剪辑软件发现读不了就是这个原因。宁可体积大一点,也别到用的时候才发现打不开。
如果是纯素材整理、只看内容不在意画质,可以把分辨率压到 720:
-S "res:720" --recode-video mp4--recode-video mp4会强制转码统一格式,代价是耗时增加。我一般在“批量收素材”的时候才开,日常备份不开。
4.3 断点续传与失败重试:让长任务不怕断
跑几百条的任务,中途断网、断电、系统休眠都是常事。默认行为是重跑的时候已完成的跳过、未完成的从头开始——这意味着一个下了 90% 的大文件,断了就得从 0 再来。加上-c就能续传:
-c --retries 10 --fragment-retries 10 --retry-sleep 3--retry-sleep 3是每次重试间隔三秒,指数退避策略在工具里是内置的,这个值给的是基础间隔。对于偶发性的连接重置,这个组合基本能无感恢复。
还有一个参数值得知道:--no-part。默认情况下正在下载的文件会带个.part后缀,下载完成才改名。这个设计本来是为了防止半成品被误当作成品,但如果你的目录要同时被别的程序扫描(比如媒体库软件),那些.part文件会造成干扰。要不要关掉看你的下游怎么用,我倾向于保留默认,稳。
5. 常见故障排查与踩坑实录
5.1 高频报错速查表
下面这张表是我这几年攒下来的,按出现频率排序。遇到问题先对号入座,八成能在里面找到:
| 现象 | 大概率原因 | 处理方式 |
|---|---|---|
| 只下到十几条就结束 | 列表懒加载被截断,登录态未生效 | 检查 cookie 是否有效,先跑探测命令看条目数 |
大量HTTP Error 403 | 链接带时效签名,过期了 | 删掉.part文件重跑,别用缓存的地址 |
| 下来的文件没有声音 | 缺 ffmpeg 或路径没配 | 装 ffmpeg,确认ffmpeg -version能跑通 |
| 提示格式不可用 | -f表达式写死了某个具体格式 | 改成-f "bv*+ba/b"这种带回退的写法 |
| 文件名乱码或报错 | 标题含系统非法字符 | 加--windows-filenames或--restrict-filenames |
| 重跑时重复下载 | 忘了加--download-archive | 补上归档参数,路径要固定 |
| 追更时漏了很多条 | --break-on-existing提前刹车 | 定期跑一次不带该参数的完整扫描补漏 |
| 跑到一半卡死不动 | 并发过高触发限流 | 降-N,加--sleep-requests |
| 归档文件有了但文件不在 | 下载成功但落盘失败,或被手动删了 | 从归档文件里删掉对应 ID 重新下 |
5.2 我踩过的三个坑,希望你绕开
第一个坑:归档文件路径写成了相对路径。这个坑特别阴——你在 A 目录跑的时候一切正常,某天从 B 目录用 crontab 触发,相对路径解析到别处去了,归档文件变成一个空的新文件,于是整个账号的内容被从头重下一遍。现在我所有脚本里的归档路径、输出路径、cookie 路径一律写绝对路径,宁可长一点。这个教训值几百 G 流量。
第二个坑:以为--write-info-json只写一次。它默认是每次下载都写,配合续传的时候可能覆盖掉之前手动改过的元数据文件。如果你有后期编辑元数据的习惯,加--no-overwrites保护一下。我给素材做标注的时候被覆盖过两次,从那以后这个参数就成了标配。
第三个坑:磁盘空间。1080p 的短视频单条几十 MB 看着不多,但一个账号几百条加上合集,很容易堆到几十 G。跑之前先估一下:单条平均体积乘以条目数,再打个 1.5 倍的余量。我现在的做法是脚本开头加一句磁盘检查,剩余空间低于 20G 就直接退出并提示,避免跑到一半写满磁盘留下一堆半成品。
#!/bin/bash AVAIL=$(df -m /path/to/vault | awk 'NR==2 {print $4}') if [ "$AVAIL" -lt 20480 ]; then echo "磁盘剩余 ${AVAIL}MB,低于阈值,退出" exit 1 fi这段就十几行,但能救你一次深夜的手忙脚乱。
5.3 一个容易被忽略的细节:文件名长度
不同系统对单段文件名的长度限制不一样,从 255 字节到 143 字节都有。中文字符在 UTF-8 下占三个字节,所以一个六十个汉字的中文标题,光标题就 180 字节,加上日期、ID、扩展名很容易超限。表现是下载成功但改名失败,留下一个.part或者一串乱码名。
解决办法就是模板里统一加截断,%(title).40s甚至%(title).30s都行。别心疼,标题截断了信息还在info.json里,真要查随时能查回来。
6. 边界与长期维护:别让脚本跑在你控制之外
6.1 用得久不如用得对
工具本身没有立场,但用的人得有。我给自己定的三条线,写在这里供参考。
第一条:只处理自己有权处理的内容。自己账号发的东西随便备份;别人授权给你的,留好授权记录;公开可引用的内容,只做个人学习和研究用途的本地留存。除此之外的一律不碰,这不是技术问题,是基本的做事习惯。
第二条:不对外分发、不做二次传播。本地留档是一回事,把它传到公开渠道、打包给一群人、拿去商用是另一回事,性质完全不同。技术上的可能性不等于使用上的合理性,这条界线很多人一开始就模糊了。
第三条:控制请求频率。--sleep-requests和-N那两个参数不只是为了自己跑得稳,也是别给对方添麻烦。批量抓取对服务端是有成本的,礼貌一点,长期来看对所有人都有好处。我做定时任务的时候会把时段错开整点,纯属个人习惯,但也确实少遇到过限流。
6.2 长期跑下去需要盯的三件事
一套流程能跑一次不算本事,能跑一年才算。我把需要定期检查的东西压缩成三件。
一是工具版本。这个必须每月看一眼,平台改版是常态,社区修复通常很快,但你得主动升级才会生效。做法很简单,在追更脚本最前面加一行升级命令,失败就跳过不阻塞:
yt-dlp --update-to stable 2>/dev/null || true二是归档文件的一致性。被下载的文件名里的 ID 和归档文件里的 ID 应该是对得上的。每隔一段时间抽几条做个交叉验证,确认归档文件里有的、磁盘上也确实在。我发现过一次归档记了但文件被误删的情况,就是因为某次清理脚本写错了通配符。
三是日志的可用性。别把日志直接丢进黑洞,至少保留最近三十天,而且失败信息要能一眼看出来。我的做法是在追更脚本末尾加一句汇总:
if [ -s /path/to/failed.log ]; then echo "本次有失败条目,详见 failed.log" | mail -s "追更异常" you@example.com fi说到最后,我个人在实际操作里的体会是:这套东西的价值不在于“三条命令有多快”,而在于它把一件重复劳动变成了一个可以被信任的日常任务。你不需要每天想着它,它每天早上七点自己跑一遍,把新的内容安静地放到该在的位置,偶尔出问题给你发个提醒。真正需要你动脑子的地方只有两处——第一次把目录和参数设计对,以及每隔一段时间回头看看它有没有悄悄跑偏。剩下的,交给那三行命令就好。