当前位置:首页 > 科技 > 正文

用Golang扒拉一年级365故事视频?这事儿我真干过

  • 科技
  • 2026-06-29 01:12:19
  • 252
摘要: 当Golang遇上“一年级365故事”视频:一个老父亲的折腾实录先坦白,我不是什么编程大神,就是个爱折腾的普通老爸,闺女刚上一年...

当Golang遇上“一年级365故事”视频:一个老父亲的折腾实录

先坦白,我不是什么编程大神,就是个爱折腾的普通老爸,闺女刚上一年级,班主任发了个通知,说学校推荐看《一年级365故事》视频,每天一个,培养阅读习惯,听起来挺好的对吧?但问题来了——这视频藏在某个APP里,每天只能在线看,一卡一卡的,闺女急得直跺脚

我寻思着,要不把视频扒下来存本地?这样她随时能看,还不费流量,说干就干,打开电脑,顺手就敲开了GoLand。嗯,Go语言搞这个正合适,编译快,并发强,写个小工具分分钟的事

先理清楚需求:到底要抓什么?

我先把《一年级365故事》这个系列摸了个底:一共365个故事,每天一个,每个视频大概5-10分钟,视频格式是mp4,来源嘛……据说是某教育平台的加密流媒体(这里不点名了,懂的都懂)。

我的目标很明确:

  • 自动下载:不需要我每天手动点
  • 批量处理:一次搞定365个
  • 保持清晰度:别给我720p以下糊弄孩子
  • 命名规范:第1天《小马过河》.mp4 这种格式

说实话,一开始我琢磨着用Python,—Python部署起来太麻烦,还得搭环境,万一电脑重装又得从头来,Go语言就爽多了,单文件编译,扔哪个电脑都能跑。

动工之前:得先搞明白它怎么播放的

我打开开发者工具(F12),切换到Network面板,刷新页面,看它请求了啥,发现视频是通过m3u8文件分片加载的——这年头大部分视频都用HLS协议了

但有个坑:它的m3u8链接有时限,大概15分钟就过期,这意味着我不能把链接存下来慢慢下,得在短时间内搞定。

所以我的Golang小工具得干这么几件事:

  1. 模拟登录(拿到cookie)
  2. 解析页面,找到每个视频对应的m3u8地址
  3. 并发下载所有TS分片
  4. 合并成完整的mp4
  5. 按照“天数+故事名”重命名

听起来有点绕,但拆开来看,每一步都不难。

代码开搞:从登录开始

先写个简单的http客户端,带上cookie去访问,Go的net/http包足够用了,我就没上第三方库,登录部分最烦——它有验证码,我没法自动识别,后来想了折中办法:手动登录一次,把cookie复制出来,写死在代码里,反正我自己的账号,不丢。

代码大概长这样(简化版):

// 伪代码示意,具体不贴了
client := &http.Client{}
req, _ := http.NewRequest(“GET”, url, nil)
req.Header.Add(“Cookie”, “你的cookie”)
resp, _ := client.Do(req)

然后解析页面,找视频列表,这里我用的是goquery——Go版的jQuery,解析HTML不要太舒服,每个故事标题对应一个链接,点进去就能找到播放页。

关键一步:从播放页挖出m3u8

播放页里一般会有一个video标签,src指向一个接口,这个接口返回真正的视频地址,有的平台会藏得更深,把m3u8写在JavaScript变量里,这时候就得用正则硬啃了。

比如有时候是这样:

var videoUrl = “https://cdn.example.com/2024/01/01/hd.m3u8

我用正则把它抠出来:

re := regexp.MustCompile(`videoUrl = “(.*?m3u8)”`)
match := re.FindStringSubmatch(html)

注意:m3u8可能包含多个分辨率,比如720p、1080p,我选最大的那个,给孩子看,画质不能含糊。

并发下载:Go的强项来了

拿到m3u8文件后,里面列了一堆ts分片的地址:

#EXTINF:2.000,
seg-1.ts
#EXTINF:2.000,
seg-2.ts
...

我写了个函数,把这些地址全抓下来,然后用goroutine并发下载,Go的并发机制简直为这种场景量身定做:

  • 开10个goroutine拉片
  • 用channel收集结果
  • 全部下载完毕后再合并

速度飞起——原来一个5分钟的视频,串行下载得等1分钟,现在10个并发,10秒搞定

合并TS分片也简单:按顺序把所有ts文件内容写到同一个文件里,Go的io.Copy函数一次搞定:

out, _ := os.Create(“output.mp4”)
for _, seg := range segments {
    in, _ := os.Open(seg)
    io.Copy(out, in)
    in.Close()
}
out.Close()

命名归档:让闺女一眼看懂

下载下来的视频文件名是乱码,我得重命名成“第01天-小马过河.mp4”这种格式,我从页面标题里提取了“第X天”和故事名,然后拼接:

day := extractDay(title)    // “第01天”
name := extractName(title)  // “小马过河”
newName := fmt.Sprintf(“%s-%s.mp4”, day, name)
os.Rename(“temp.mp4”, newName)

为了让闺女方便找,我还按月份建了文件夹:9月/第01天-小马过河.mp4,这样她一下子就能看到这个月讲了啥。

遇到的坑:真不少

说几个印象深的:

原因 怎么解决的
m3u8链接频繁失效 平台加了防盗链,链接只有15分钟有效 拿到链接后立即启动下载,不拖沓
部分视频有字幕合并问题 字幕是独立文件,和视频流分开 下载后手动用ffmpeg合并(这个没集成到代码里,太麻烦)
并发下载导致磁盘IO过高 同时写10个文件,HDD顶不住 限流,只开5个goroutine
备案问题 有些平台需要referer验证 在请求头里加上Referer字段

最蛋疼的是,我写了三天代码,结果发现某宝上有现成的一年级365故事视频U盘,才19.9包邮…… 但自己动手的成就感,不是钱能衡量的。

运行效果:闺女挺满意

代码写完后,我把它编译成Windows可执行文件(我主力机是Win10),放在桌面上,双击运行,等个十几分钟,365个视频全部下载到“一年级365故事”文件夹里了。

闺女打开文件夹,看到整整齐齐的列表,眼睛都亮了:

  • 第01天-小马过河.mp4
  • 第02天-龟兔赛跑.mp4
  • 第03天-乌鸦喝水.mp4
  • 第365天-年的故事.mp4

她小手一点,视频就在本地播放器里流畅播放,再也不卡了,晚上她一口气看了5个故事,还拉着我讨论“为什么乌鸦要扔石头”。

后来我把这个工具分享到家长群里,几个懂编程的爸爸拿去改巴改巴,适配了其他几个教育平台,有个爸爸更神,加了个批量转码功能,还写了个GUI界面——虽然我觉得命令行更酷

其实吧,这事技术含量不算高,但用Golang这么一搞,确实省心,编译出来一个小文件,扔到NAS上定时任务跑,每天自动下载当天的故事,闺女现在每天早上刷牙洗脸时,就打开平板看一个,跟打卡似的。

最后说几点实在的

如果你也想搞类似的事,记几个要点:

  • 优先检查教程:之前有不少开发者写过类似项目,搜一下“Go m3u8下载”能找到现成代码,别重复造轮子
  • 留意平台协议:有的平台用的是DASH协议(m4s分片),跟m3u8的TS分片处理方式不同
  • 别贪多:一次抓太多视频,容易被封IP,我设置了每下载一个视频sleep 2秒
  • 文件校验:下载完后检查文件大小,小于500KB的基本是坏的,重新下载

哦对了,文件名最好别带冒号、问号这些特殊字符,Windows下会报错,我踩过这个坑,文件名里有“?”直接崩了。

工具代码现在还躺在我的GitHub私有仓库里,偶尔更新一下,闺女上二年级了,又开始催我搞“二年级365故事”了——看来这活,我还得继续干下去

用Golang扒拉一年级365故事视频?这事儿我真干过