用Go语言折腾一整年,我如何用365天视频读完红楼梦
- 能源
- 2026-07-07 22:42:13
- 263
我从没想过自己会和《红楼梦》扯上关系,更没想过会用Go语言去“读”它,这事儿得从去年元旦说起。
那天我瘫在沙发上刷手机,刷到一条推送:“一年365天,每天一分钟,看完《红楼梦》”,我点进去一看,是个视频合集,每天更新一小段,配上原文朗读和画面,我当时想,这主意不错啊,反正我也没耐心翻那厚厚一本,结果看了三天就忘了。
但后来发生了一件事,让我彻底入了坑——我发现自己用Go写的一个小工具,居然能帮我“自动”看这些视频。
为什么是Go?为什么是《红楼梦》?
先说视频,那系列视频叫“一年365天视频红楼梦”,就是每天发一个一分钟左右的短视频,按原著章节顺序走,比如第一回“甄士隐梦幻识通灵”能拆成七八个视频,每个讲一个小场景,365天刚好能覆盖全书一百二十回,外加一些背景解读。
这形式其实挺聪明的——每天只占用你一分钟,压力小,容易坚持,但我这人有个毛病,一旦觉得某件事有规律可循,就忍不住想用代码去优化它,比如我就想:能不能每天自动下载当天的视频?能不能把视频里的音频转成文字?能不能把这些文字按日期整理成笔记?
Go语言这时候就派上用场了,它编译快、部署简单、并发处理能力强,最关键的是,Go的生态里有很多现成的库,拿来做视频下载、音频转文字、文本处理这些事,特别顺手。
我第一个想法是写个定时任务,每天自动抓取视频,但很快发现这事没那么简单。
第一版:用Go写个“视频下载器”
我一开始的思路是这样的:每天凌晨用Go写个cron定时任务,去那个视频平台检查有没有新视频更新,如果有就下载下来。
我用net/http包发请求,用regexp包解析HTML,市面上很多视频网站的反爬措施都不弱,但那个“一年365天视频红楼梦”系列的视频是公开可看的,而且视频地址直接写在页面源码里,没加密,这让我省了不少事。
// 伪代码,实际写起来比这长十倍
func fetchDailyVideo() {
url := "https://xxx.com/daily/hongloumeng"
resp, _ := http.Get(url)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// 用正则找到视频地址
re := regexp.MustCompile(`"videoUrl":"(.*?)"`)
matches := re.FindStringSubmatch(string(body))
// 下载视频
downloadVideo(matches[1], time.Now().Format("2006-01-02"))
}
第一版跑起来后,我发现了一个问题——视频是能下载了,但我根本没时间看,每天一分钟的视频,我下载了五十多个,一个都没打开过。
这不就是典型的“下载癖”吗?下载即收藏,收藏即吃灰。
第二版:把视频变成文字,用Go做音频转写
我决定换个思路,既然视频是讲《红楼梦》的,核心内容是音频里的旁白和字幕,那为什么不直接转成文字呢?
Go语言没有原生的语音识别能力,但可以用第三方的API,我接入了阿里云的通义听悟(当时还叫“语音识别”),把视频里的音频轨提取出来,转成文本。
音频提取这块,Go的ffmpeg绑定库特别好用,我写了个函数,把MP4文件转成16kHz的PCM音频,然后分段传给识别接口。
// 提取音频并转写
func transcribeVideo(videoPath string) (string, error) {
// ffmpeg提取音频
cmd := exec.Command("ffmpeg", "-i", videoPath, "-vn", "-ar", "16000", "-ac", "1", "temp.wav")
cmd.Run()
// 读取音频文件
audioData, _ := ioutil.ReadFile("temp.wav")
// 调用语音识别API
result, err := speechToText(audioData)
if err != nil {
return "", err
}
return result, nil
}
这样做的好处是,我每天只需跑一次脚本,就能得到当天的《红楼梦》知识点文字,但我很快发现,一分钟的音频转成文字后,只有两三百字,内容太少了。
第三版:用Go做内容聚合和结构化
只转文字还是不够,我想让这些文字变得更有用——比如按人物、情节、诗词分类。
《红楼梦》里人物众多,光是有名有姓的就有四百多个,我写了个简单的实体识别模块,用map做大词表匹配,比如在文本里找到“黛玉”、“宝钗”、“宝玉”、“凤姐”这些关键词,就自动打上标签。
// 简单的关键词匹配
var characterMap = map[string]string{
"黛玉": "林黛玉",
"宝钗": "薛宝钗",
"宝玉": "贾宝玉",
"凤姐": "王熙凤",
// 更多...
}
func tagCharacters(text string) []string {
var tags []string
for keyword, fullName := range characterMap {
if strings.Contains(text, keyword) {
tags = append(tags, fullName)
}
}
return tags
}
我还用Go的goquery库(类似jQuery的HTML解析器)去抓取维基百科和百度百科里关于《红楼梦》的条目,把这些视频文本和百科知识做关联,比如某天视频讲的是“刘姥姥一进荣国府”,我就自动把刘姥姥的百科词条摘要也拉进来,作为补充阅读。
第四版:做一个本地知识库,每天自动推送
到这一步,我已经有了一个每天更新的《红楼梦》小专栏了——每天的视频文字、人物标签、相关百科摘要,都按日期存成Markdown文件。
然后我用Go的embed功能(Go 1.16引入的),把整个文件夹嵌入到一个小型HTTP服务器里。
//go:embed notes/*
var notes embed.FS
func main() {
http.Handle("/notes/", http.FileServer(http.FS(notes)))
log.Fatal(http.ListenAndServe(":8080", nil))
}
这样我就可以在浏览器里每天打开localhost:8080/notes/2024/01/01.md,看到当天的学习内容。
后来我又加了个小功能:用Go的gopls(语言服务器协议)做了一个简单的TUI界面,每天打开终端,输入honglou,就能看到当天更新的段落,还能按人物筛选。
比如我想看看“这周关于王熙凤的内容有哪些”,程序会扫描所有笔记,找出包含“王熙凤”或“凤姐”的条目,按时间排序显示。
踩过的坑和真实的体验
说句实话,这整个过程远没有我写得这么顺利,中间出了无数bug。
最大的坑是语音识别,方言、背景音乐、语速问题,都会导致转写错误,比如有一集视频里旁白说“贾母携众人拜佛”,结果识别成了“贾母携众人败毒”,类似的笑话很多,我后来不得不加了个校对步骤,把常见的错误词手动映射成正确的。
还有一个坑是视频平台改了接口,用了两个月后,对方更新了前端代码,视频地址的匹配规则变了,我花了两个小时重新调试正则表达式,当时真想把这项目删了拉倒。
但奇怪的是,正是这些“折腾”的过程,让我对《红楼梦》的内容记得特别牢,因为每天都要处理这些文本,我得反复去确认内容是否准确,比如有一回视频讲“晴雯撕扇”,文字转写出来是“晴雯撕伞”,我查了原文才发现是“扇子”,这种纠错的过程,反而加深了记忆。
结果怎么样?
到现在,365天过去了,我其实没完整“看”完那365个视频——大部分时间都是靠自动下载、转写、聚合来处理的,但最终我得到了: 类型 | 数量 | |---------|------| | 视频转写文字 | 365篇 | | 百科关联条目 | 120余篇 | | 人物标签数据 | 400+人物 | | 自用笔记文件 | 365个md文件 | | 核心诗句提取 | 80余首 |
这些文件加起来才几十兆,但里面包含了《红楼梦》全书一百二十回的梗概、重要对话、诗词赏鉴,还有我通过Go代码自动生成的交叉索引。
如果你问我还想不想翻那本纸质的《红楼梦》——当然想,但这个用Go语言搭建的“365天视频红楼梦”系统,让我在一年里至少能说出“元春省亲”发生在第几回、“宝钗扑蝶”和“黛玉葬花”哪个在前头,对于一个曾经连“金陵十二钗”都数不全的人来说,这已经值了。
现在每天早上一杯咖啡,打开终端看一眼那个程序自动准备的内容,突然觉得——写代码的人读《红楼梦》,可能比别人更快一点。

下一篇:世界杯最贵的一场较量来了