当前位置:首页 > 体育 > 正文

用Golang写个爬虫,蹲一场厄瓜多尔VS韩国的直播,结果我悟了

  • 体育
  • 2026-08-31 07:08:03
  • 4
摘要: 事情是这样的,昨儿个晚上,我一边啃着鸭脖,一边刷手机,看到“360厄瓜多尔VS韩国视频直播”这几个字挂在热搜上,我寻思,这届世界...

事情是这样的,昨儿个晚上,我一边啃着鸭脖,一边刷手机,看到“360厄瓜多尔VS韩国视频直播”这几个字挂在热搜上,我寻思,这届世界杯(或者友谊赛,管他呢)怎么这么会挑时间,专挑我这种白天写代码,晚上只想躺平的程序员精神最脆弱的时候来。

本来想直接找个网页看就完了,但手贱,点进了一个所谓的“360直播”链接,好家伙,弹窗比我家楼下的广告还密集,画质糊得跟马赛克似的,关键是一卡一顿,正好在厄瓜多尔球员准备起脚射门那一下,画面定格,…加载失败。

我这暴脾气,当时就上头了,咱是干嘛的?写Golang的,与其跟这破网页斗智斗勇,不如自己动手,丰衣足食。用Golang写个爬虫,去抓直播源,这不比跟浏览器插件较劲来得硬核?

第一步:别急着写代码,先想清楚“抓”什么

我知道你们想说什么:“直接抓流媒体地址啊,软哥。” 对,也不对,直播流这玩意儿,加密、防盗链、时效性,三座大山压下来,你空手去抠,抠出来的多半是403或者一个空壳子。

费曼学习法告诉我们,你得能用最直白的话,把一个事儿讲给小白听,那我的思路就是:我不直接去抠那个最终播放的.m3u8或者.flv,那太难了,我换个策略,我去抓“信息载体”

某个频道页的HTML里,可能藏着iframe,那个iframe里才有真正的播放器,或者,某些数据接口会返回json,里面有playUrl字段,我的目标,是先把这个页面结构搞明白。

package main
import (
    "fmt"
    "io"
    "net/http"
)
func main() {
    // 我随便写的目标URL,你懂的,要换成能用的
    url := "https://example.com/live/ecuador-vs-korea"
    client := &http.Client{}
    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")
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("请求失败:", err)
        return
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body)[:2000]) // 先打印前2000个字符看看
}

看到没,这就叫“先探测敌情”,你得先确认,服务器有没有拒绝你,返回的是不是200里有没有关键词,比如livem3u8video之类的。

第二步:解析HTML,像个侦探一样找线索

光打印出来不行,得解析,用goquery或者golang.org/x/net/html都行,我习惯用goquery,因为选择器写起来像jQuery,亲切。

我得从那一堆乱糟糟的标签里,找到<script>标签里嵌入的window.__INITIAL_STATE__,或者直接找到<video>标签的src属性,数据不在src里,而在某个data-*自定义属性里,这都得试。

标签/属性 可能藏的东西 我的备注
<iframe src="..."> 嵌套播放器页面 最常见,需要二次跳转
<video src="..."> 直接可播放地址 最理想,但少见
script 里的 var playUrl = "..." 动态生成的地址 需要正则或者json解析
<meta property="og:video" content="..."> 分享用的视频地址 偶尔会有惊喜

我就拿那个meta标签举例,有时代码会写成这样:

import (
    "fmt"
    "github.com/PuerkitoBio/goquery"
    "strings"
)
func findVideo(doc *goquery.Document) {
    doc.Find("meta").Each(func(i int, s *goquery.Selection) {
        property, _ := s.Attr("property")
        if strings.Contains(property, "video") {
            content, _ := s.Attr("content")
            fmt.Printf("发现疑似视频地址: %s\n", content)
        }
    })
}

就这么简单粗暴。

第三步:处理重定向和防盗链(这里最坑)

你以为找到m3u8地址就完事了?Naive! 这是整个“360厄瓜多尔VS韩国视频直播”抓取里最恶心的一步。

对方服务器会检查RefererOriginCookie,你光秃秃地拿个HTTP GET去请求,人家直接给你返回一段错误报文,这时候,你就得在Header里补全信息。

  • Referer: 必须设置成你刚才找到播放器的那一页的地址。
  • User-Agent: 必须跟你第一次请求时的一致。
  • Cookie: 如果是动态生成的,你前面几轮请求中,服务端通过Set-Cookie给你的,你得存下来,存到jar里,下次带上。

我管这叫“带着身份牌去串门”,你要是空着手(没Referer),人家保安(服务器)肯定不让你进(返回403)。

// 伪代码,示意一下设置Referer
req.Header.Set("Referer", "https://example.com/live/player?uuid=123")
req.Header.Set("Accept", "application/json, text/plain, */*")
// 还要管理Cookie,用net/http/cookiejar

第四步:流媒体不止m3u8,还有flvrtmp(不过现在少了)

现在主流是HLS.m3u8),但有些“360直播”源用的是HTTP-FLV,这就有点麻烦,m3u8你只要解析里面的.ts分片,一个个下载拼接或者交给ffmpeg处理就行,但FLV是一个长连接,你得自己解析里面的Tag

那我不如直接ffmpeg拉进来,用os/exec调用外部命令,把m3u8地址喂给ffmpeg,让它去处理,我就喝口茶等结果。

import "os/exec"
func pullStream(url string, output string) {
    cmd := exec.Command("ffmpeg", "-i", url, "-c", "copy", output)
    err := cmd.Run()
    if err != nil {
        fmt.Println("拉流失败,可能地址过期了", err)
    }
}

看,问题就出在“地址过期”。 就像厄瓜多尔那个进球机会,转瞬即逝,直播流地址通常有有效期,你费了半天劲抓到了,可能过了5分钟就不能用了,我的代码得循环去刷新获取新地址,然后断线重连。

第五步:360”这个前缀,我多嘴一句

这“360”多半是挂着羊头卖狗肉,可能是某个用了奇虎360统计服务的第三方直播平台,或者纯粹是名字带了数字。但你发现没有,这正印证了Golang在爬虫领域的尴尬地位:能爬,但总得跟这种混乱的生态斗争。

如果你真想看高清的“厄瓜多尔VS韩国”视频直播,我劝你别折腾了。去FIFA官网,或者正规的体育平台(比如ESPN、CCTV5+,或者央视影音),那些地方的流地址虽然也加密,但至少有迹可循,不会给你搞一堆弹窗广告和假链接。

我这一晚上,代码写了一半,球赛没看成,光看服务器上的返回状态码了。200302403502,跟看心电图似的。

但话说回来,写爬虫这事儿吧,就跟看球赛一样,你得有耐心,得能沉住气,从一堆毫无章法的数据里,找到那个最关键的突破口。

好嘞,我得去用Golang写个定时任务,每秒钟去探测一次那个直播地址是不是失效了,免得错过了下半场的进球集锦,这就是程序员的浪漫——不直接看球,但我要让代码替我看球。

用Golang写个爬虫,蹲一场厄瓜多尔VS韩国的直播,结果我悟了