百度网盘里存了整整一个“365视频教程”的文件夹,想着“反正存了就是学了”,结果一年过去了,那文件夹还是原封不动地躺在那里,连点开的欲望都没有,我懂,真的懂,因为我自己就干过这事儿,而且不止一次,直到有一天我实在受不了了,决定用Go语言写个小工具,把这些教程按目录结构批量下载到本地,顺便按课程名自动重命名文件夹——这才算真正把“存在网盘里”变成了“能在手机里随时翻出来看”。
先说结论:Go语言干这事儿,真的挺顺手
你可能会问,为什么偏偏选Go?Python不好吗?Node.js不香吗?说实话,都行,但Go有几个点特别戳我:
- 编译出来就是个二进制,扔到服务器上跑,连环境都不用配,我的老电脑是Windows 7,装个Go交叉编译一下,直接生成一个exe,双击就能跑。
- 并发是原生支持的,下载教程这种IO密集型的活儿,goroutine一开,同时下5个文件,速度直接起飞。
- 标准库够用,net/http、os、path/filepath这些,基本覆盖了需求,不用额外引第三方包,省心。
我得承认,百度网盘的API是有官方文档的,但它的下载接口需要用户自己带token,而且限速严重,我一开始用官方API,下载速度稳定在100KB/s,下完一个500MB的视频得等一个多小时,后来我换了思路,直接模拟网页端的下载链接,配合多线程分片下载,速度能跑到2-3MB/s,这个方案虽然有点“野路子”,但对个人使用来说,完全够了。
第一步:拿到网盘文件清单,用Go解析JSON
百度网盘有个 /file/list 接口,你只需要带上你的cookie或者access_token,就能拿到某个目录下的所有文件信息,返回的是个JSON数组,里面包含文件名、文件ID、大小、修改时间这些字段,我的思路是:
- 先用一个递归函数,把整个“365视频教程”目录下的所有子目录和文件都扫一遍
- 每扫到一个目录,就记录下它的完整路径(拼接父目录)
- 每扫到一个文件,就存下它的文件ID和文件名
这里有个小坑:百度网盘的接口有频率限制,你如果疯狂递归调用,很容易被临时封禁,所以我在每次请求之间加了 time.Sleep(200 * time.Millisecond),稳得很。
代码大概是这样的(简化版,但思路是完整的):
type NetdiskFile struct {
FileID int64 `json:"fs_id"`
FileName string `json:"server_filename"`
IsDir bool `json:"isdir"`
Path string `json:"path"`
Size int64 `json:"size"`
}
func walkDir(client *http.Client, token string, path string, result *[]NetdiskFile) {
// 拼接请求参数,调用 /file/list
// 解析响应bytes,按isdir字段判断是文件还是目录
// 如果是目录,递归调用自身;如果是文件,append到result
}
别笑,这代码我写了整整两天,不是因为逻辑复杂,而是因为我一开始不懂接口返回里有个 isdir 字段,我全当文件处理了,结果下载了一堆空文件夹,气死。
第二步:生成下载链接,别用官方那个慢的要死的接口
官方下载接口是 /file/download,但那个速度……算了不说了,我后面发现,百度网盘有个秒传链接的逻辑:只要文件指纹(MD5)和服务器上某个文件一致,就能用这个接口直接生成一个临时下载链接,这个链接的有效期是30分钟,而且支持HTTPS分段下载。
我的做法是:
- 对每个文件,先调用
/file/metadata拿到它的dlink(这个dlink就是下载直链,但需要带签名参数) - 把dlink拼上
&ua=pc,然后发起HTTP GET请求,注意带上Range头来分片下载
这里有个大坑:dlink虽然拿到了,但如果你直接请求,会返回403,因为百度网盘要求请求头里必须带 Referer: https://pan.baidu.com/ 和 User-Agent 是特定的浏览器字符串,你直接在Go里设置这两个header,就过了。
分片下载的Go代码长这样:

func downloadPart(client *http.Client, url string, start, end int64, output *os.File) error {
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Referer", "https://pan.baidu.com/")
req.Header.Set("User-Agent", "Mozilla/5.0...")
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end))
resp, err := client.Do(req)
defer resp.Body.Close()
// 用io.Copy到output,注意seek到start位置
}
并发控制我是这么做的:创建个信号量channel,容量设成3,每启动一个goroutine下载一个分片,就往里塞一个值;下载完了取出来,保证同时最多3个分片在跑,太贪心不行,5个分片同时下,百度网盘会直接断连,别问我怎么知道的。
第三步:文件名里的乱码,整治一下
365视频教程的命名其实挺规整的,第01课-冒泡排序算法详解.mp4,但偶尔会有那种 资料???.pdf 或者 课程(完结)_2023版.zip 这种带特殊字符的,Windows下文件名不能有 \ / : * ? " < > |,所以我得在写到本地前,把非法字符替换成下划线,Go有个 path/filepath 的 Clean 函数,但它不处理非法字符,我写了个小函数:
func sanitize(filename string) string {
illegal := []rune{'/', '\\', ':', '*', '?', '"', '<', '>', '|'}
var b strings.Builder
for _, r := range filename {
ok := true
for _, i := range illegal {
if r == i { ok = false; break }
}
if ok { b.WriteRune(r) } else { b.WriteRune('_') }
}
return b.String()
}
文件大小超过4GB的,比如某些长达两小时的全英文无字幕版,记得用 int64 来存长度,别用 int,我第一版就用错了,下载到80%的时候程序直接报 size negative 的错误,当时我还以为是网盘服务器出问题了,排查了一晚上。
那些年我为了下载速度做的“坏事”
其实百度网盘官方是有 SVIP 高速下载通道的,但那是要花钱的,我一个白嫖党,只能曲线救国,我发现,如果你在网盘网页版点“下载”,它会生成一个带参数 ?sign=xxx 的链接,这个链接通过HTTPS的RSA加密签名,有效期大约10分钟,我的Go程序做的就是:模拟这个签名过程,然后用同一个IP,同时开4个groutine去请求同一个文件的4个不同分片,只要你的网速够快,这4个分片是能同时到达的,下载速度就是单线程的4倍。
不过别太贪,有次我开了8个线程,被百度风控盯上了,直接封了我IP一个小时,后来我学乖了:每个文件下载前先sleep 1秒,让服务器觉得我是个人类在操作。
弄个简单的命令行界面,加个进度条吓唬人
我写了个简单的终端UI,用 fmt.Printf 配合 \r 回车符,实现单行刷新进度条,效果是这样的:
第01课-冒泡排序算法详解.mp4 42.3MB / 128.6MB [=====>------] 32.9% 1.8MB/s
代码就几行:
func showProgress(downloaded, total int64) {
percent := float64(downloaded) / float64(total) * 100
bar := strings.Repeat("=", int(percent/2)) + strings.Repeat("-", 50-int(percent/2))
fmt.Printf("\r%s %.1f%% %6.1fMB/s", bar, percent, currentSpeed())
}
这个 currentSpeed() 我是用了一个滑动窗口,每500毫秒采样一次已下载字节数,然后算差分,懒人做法是直接用 net/url.QueryEscape 转一下文件名就完事,但那样进度条里的中文会乱码,所以我用了 unicode/utf8 库手动处理。
遇到无权限的文件怎么办?假装没看到
有些365视频教程可能是分享的,但你用的是自己的账号,没有那个文件的下载权限,百度网盘的接口会返回 errno: -36 表示权限不足,我的做法是:把错误记录到 err.log,然后跳过这个文件,继续下一个,因为你永远不可能通过写代码强行下载自己没有权限的文件——有那功夫,不如去私信分享者要个转存。
我甚至写了个好笑的函数:
func isPermissionDenied(err error) bool {
return strings.Contains(err.Error(), "-36")
}
然后每碰到一次,就 fmt.Println("跳过无权限文件:", filename) ,顺便在系统通知里弹个“失败但继续”的提示,毕竟生活嘛,总得允许有些东西得不到。
最后聊聊这个流程整体上的“健康度”
我用了大概一个周末,把整个工具跑通了,最终下载了 231个视频,总大小 87.4GB,耗时累计约9小时(包含被限速和暂停的时间),下载完以后,我用 ffprobe 检查了几十个文件的完整性,没有一个报错,说明分片下载+拼接的逻辑是稳的。
但我必须说清楚:这个工具只适合下载你自己网盘里已有的文件,或者你有转存权限的文件,用来下载别人分享但没给转存权限的东西,是行不通的,我也没搞什么“破解”“绕过登录”的黑魔法,所以它在法律和道德上,都是个“灰色但没犯法”的存在。
最后的最后,我把生成的exe文件丢到了桌面,用鼠标双击,它会弹出一个CMD窗口,打印进度,然后自己关闭,每次我看到那个窗口跑完,都觉得自己好像真的把那些“存了就是学了”的课,变成了一堆实实在在的mp4文件,心里踏实多了。
本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://hljbesthome.com/qiche/2399.html
评论列表(4条)
我是be365的签约作者“kyadmin”!
希望本篇文章《用Go语言写个工具,把365视频教程从百度网盘里捞出来—顺便聊聊我踩过的坑》能对你有所帮助!
本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名
本文概览:百度网盘里存了整整一个“365视频教程”的文件夹,想着“反正存了就是学了”,结果一年过去了,那文件夹还是原封不动地躺在那里,连点开的欲望...