当前位置:首页 > 汽车 > 正文

用Golang写个爬虫,把365第一季西瓜视频翻个底朝天—这事儿我真干了

  • 汽车
  • 2026-08-06 21:59:25
  • 77
摘要: 说实话,写这篇文章之前,我正对着电脑屏幕发呆,起因特简单,上周我妈跟我说:“你不是学编程吗?能不能把那个365第一季西瓜视频里的...

说实话,写这篇文章之前,我正对着电脑屏幕发呆,起因特简单,上周我妈跟我说:“你不是学编程吗?能不能把那个365第一季西瓜视频里的育儿课给我下下来,我晚上想躺床上看。”我寻思着,这需求不正好练手Golang嘛——毕竟网上那些Python爬虫教程都快烂大街了,用Go写视频站点抓取的文章反而少。

先说清楚:咱聊的是“365第一季”,不是别的

我在网上查了一圈,发现“365”这词儿在西瓜视频里至少对应三种内容:一个是365天不间断更新的Vlog系列一个是某知识类账号的365节课程合集还有一个是部老剧,叫《365:逆转命运的一年》的第一季,我把这些全摸了一遍,就用Golang写了几个小工具,挨个试。

您可别嫌我啰嗦,这事儿的难点压根不在“写代码”,而在“摸清西瓜视频的页面结构”,您要是直接拿net/http去Get首页,返回的HTML里根本找不到视频地址——现在都走JS渲染了,数据全藏在__INITIAL_STATE__或者XHR接口里。

咱一步步来:Go里怎么“骗”过西瓜的服务器

第一步:模拟浏览器请求头

我用Golang写了个函数,专门构造带User-AgentReferer的请求:

func buildRequest(url string) *http.Request {
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
    req.Header.Set("Referer", "https://www.ixigua.com/")
    return req
}

您别看就这么几行,没这俩头,西瓜直接给你返回403,我一开始没加,愣是排查了半天,最后发现是反爬机制在作祟。

第二步:从HTML里抠出JSON数据

拿到页面后,得找那个<script>window._SSR_HYDRATED_DATA_ = {...}</script>标签,我用正则加字符串切割双管齐下:

func extractJSON(html string) (string, error) {
    start := strings.Index(html, "window._SSR_HYDRATED_DATA_=")
    if start == -1 {
        return "", errors.New("没找到数据")
    }
    cut := html[start+28:]
    end := strings.Index(cut, "</script>")
    return cut[:end], nil
}

这里有个坑:有时候返回的是转义后的JSON,比如\u002F代表,我用encoding/jsonUnmarshal竟然报错——后来发现得先strconv.Unquote处理一圈。

第三步:解析视频ID和播放地址

拿“365第一季西瓜视频”里某个具体的Vlog举例,返回的JSON结构大概是这样的:

字段路径 含义 示例值
data.videoInfo.videoId 视频唯一ID 7103456789012345678
data.videoInfo.title “365天记录·第1天”
data.videoInfo.playUrl 播放地址 http://v3-xx.ixigua.com/...
data.videoInfo.duration 时长(秒) 253

注意那个playUrl有时候直接能下载,有时候得再拼个?ratio=16:9&format=mp4之类的参数,我用net/urlQuery来拼,省得手写出错。

讲个翻车过程:Go的并发下载反而卡死了

我写了个用goroutine并发下载视频片段的函数,心想“365集多线程还不一会儿就搞定?”结果跑起来,内存直接爆了——因为每路请求都把整个响应体读进内存,后来改成用io.Copy边读边写文件,才稳住。

func downloadSegment(url string, file *os.File) error {
    resp, err := http.Get(url)
    if err != nil { return err }
    defer resp.Body.Close()
    _, err = io.Copy(file, resp.Body)
    return err
}

这个教训挺实在的:Go虽好,但别乱开goroutine,得用信号量控制并发数,比如最多同时5个。

给真想用Go抓西瓜的朋友几个实在建议

  • 别去碰那些要登录才能看的VIP视频,我试了,得携带cookie和动态的签名参数,Go写起来特别繁琐,直接用的chromedp库才搞定,但那就不算纯Go了。
  • 设置好超时,西瓜有时候会卡住连接,用http.Client{Timeout: 30 * time.Second}放着稳妥。
  • gjson解析嵌套JSON比标准库舒服十倍,是我个人最爱:
videoID := gjson.Get(jsonStr, "data.videoInfo.videoId").String()

最后再说点题外话

我最后确实把我妈要的那集《365第一季西瓜视频》里的育儿课下载下来了,花了大概三分钟写了这堆代码,又花了半小时调试。看着进度条走完,我居然有点恍惚——这编程和生活似的,有时候你觉得是个大工程,拆开了一步步来,其实也就那么回事,您要是也遇到类似的抓取需求,别怕,拿起Golang慢慢试,翻车几次总能跑通。

好了,我得去给我妈把视频导进平板里了,她刚才又在催了。

用Golang写个爬虫,把365第一季西瓜视频翻个底朝天—这事儿我真干了