当前位置:首页 > 赛事分析 > 正文

用Golang写个女排大冠军杯回看小工具?这事儿我试过,真香

摘要: 先别急着写代码,咱得先聊聊女排大冠军杯这事儿说实话,我一开始对女排大冠军杯的印象就停留在“哦,那个两年一届的日本举办的比赛”,直...

先别急着写代码,咱得先聊聊女排大冠军杯这事儿

说实话,我一开始对女排大冠军杯的印象就停留在“哦,那个两年一届的日本举办的比赛”,直到去年某天半夜,我一边啃着鸭脖一边用Golang写爬虫抓比赛数据,才真正对这赛事上头了。大冠军杯啊,那可是各路豪强凑齐了的“准世界杯”,巴西、美国、中国、俄罗斯,再加上东道主日本,每队都是硬骨头,更别说它那奇葩的赛制——单循环,五场打下来,胜场多者称王,压根不给留喘气的机会。

我琢磨着,要是能用Golang写个小工具,把历届女排大冠军杯的赛事回看信息、比分、甚至技术统计都整理成结构化数据,那得多带劲,于是乎,就有了这篇边吃泡面边敲出来的文章。

第一步:得先搞明白“回看”到底要看啥

大冠军杯的历史和地位,咱得心里有数

大冠军杯从1993年搞起,每四年一届,后来改成两年一届(2005年后),它比世界杯早,又比世锦赛轻巧,但含金量绝对不低——因为参赛队都是各大洲的冠军或排名最高的队伍,所以场场都是硬仗,中国女排在这赛事上拿过两次冠军(2001年和2017年),特别是2017年那届,郎平指导带队,朱婷那记暴扣,我现在看回放都能起鸡皮疙瘩。

回看比赛,咱得看哪些“门道”?

  • 发球轮次:看看谁的发球能卡住对方一传
  • 一攻效率:到位球打不下去,那就是白搭
  • 防反质量:防起球后能不能转化成得分
  • 关键分把握:23平的时候,谁能站出来

这些数据要是光靠眼睛看,容易看花眼,用Golang写个解析脚本,把每局的技术统计表格扒下来,再对比着看比赛视频,那感觉,绝了。

第二步:Golang里怎么折腾这些赛事数据?

我的第一版代码,其实惨不忍睹

刚开始我直接用net/http去抓某个体育网站的比赛数据,结果人家反爬虫,给我返回一堆加密的JSON,我差点没把我那半碗泡面喷屏幕上,后来学乖了,用goquery去解析静态页面,总算能拿到比分和基础统计。

package main
import (
    "fmt"
    "log"
    "net/http"
    "strings"
    "github.com/PuerkitoBio/goquery"
)
func main() {
    // 假装这是2017年大冠军杯中国vs巴西的页面URL
    url := "https://example.com/volleyball/GrandChampionCup/2017/CHN-BRA"
    resp, err := http.Get(url)
    if err != nil {
        log.Fatal("抓取失败:", err)
    }
    defer resp.Body.Close()
    doc, err := goquery.NewDocumentFromReader(resp.Body)
    if err != nil {
        log.Fatal("解析失败:", err)
    }
    // 找比赛标题,顺便打印出来:= strings.TrimSpace(doc.Find("h1").First().Text())
    fmt.Printf("当前回看赛事: %s\n", title)
    // 抓每一局的比分,存到一个slice里
    var sets [][]int
    doc.Find("table.score-table tr").Each(func(i int, s *goquery.Selection) {
        cells := s.Find("td")
        if cells.Length() == 3 {
            home := strings.TrimSpace(cells.Eq(1).Text())
            away := strings.TrimSpace(cells.Eq(2).Text())
            fmt.Printf("第%d局:%s : %s\n", i, home, away)
        }
    })
    _ = sets
}

这代码跑起来虽然能打印点东西,但离“回看”的体验还差得远,我想要的,是那种能自动拉出整场比赛的每一分走势,甚至还能生成个简单的文字直播效果的东西。

进阶点:把技术统计结构化

大冠军杯的官方技术统计一般包括:扣球、拦网、发球、失误这几大项,我写了个结构体来表示:

type MatchStats struct {
    TeamName    string
    SpikeScore  int
    SpikeMiss   int
    BlockScore  int
    ServeScore  int
    OppError    int
}
func (m MatchStats) TotalScore() int {
    return m.SpikeScore + m.BlockScore + m.ServeScore + m.OppError
}

然后写个函数,去遍历页面上那些<table>标签,把每行的文本解析成数字,这活儿看着简单,但你得小心那些合并单元格和注释符号,我就在这上面栽过跟头。

第三步:让回看“活”起来

加个“逐球播放”的小功能

既然都叫回看了,那最好能有个时间轴,我用Golang的time包和goroutine写了个模拟逐球推进的程序,每过两秒,打印当前比分和关键事件(朱婷四号位强攻得分”),你还别说,配合上我下载的比赛视频,还真有点解说员那味儿。

func playByPlay(events []string) {
    for i, e := range events {
        fmt.Printf("[%02d:%02d] %s\n", i/30, i%30, e)
        time.Sleep(2 * time.Second)
    }
}

数据可视化?咱用最简单的办法

我不搞那些花哨的图表库,就直接在终端里用fmt打印个简单的“比分条”,比如用号表示得分比例,看着也直观,这玩意儿虽然不如ECharts好看,但胜在零依赖,跑起来就完事。

第四步:从”看热闹“到”看门道”

用Golang分析关键分

我把历届大冠军杯中国队的比赛数据全抓下来后,发现一个有趣的规律:只要一攻成功率超过55%,中国队基本能赢,而且到了第四局、第五局,朱婷的扣球成功率明显比前三局高,这数据虽然粗糙,但配合上回看,就能看出郎导的换人时机和暂停叫得有多准。

关于回看的“仪式感”

我有个朋友,每次看比赛回放,必须泡好茶,戴上耳机,然后把电脑亮度调到50%,他说这样才有现场感,我建议你用我的Golang工具时,也搞点仪式感——开个终端,黑底白字,一个个数字蹦出来,那感觉,比看视频还上头。

第五步:代码和数据的“毛边儿”

这事儿没那么完美,我抓的有些老数据,比如1993年那届的,网站上的比分是图片格式,我OCR识别率低得可怜,只能手动录入,而且不同年份的页面结构还不一样,有的用表格,有的用<div>嵌套,我得写好几个解析器。这就像整理旧衣柜,你得一件件地拿出来拍灰

别指望用Golang直接拉视频流,我试过用ffmpeg的Go绑定,但延迟和卡顿让人崩溃,所以我的做法是:用Golang抓静态数据,配合B站或YouTube上已有的比赛录像,手动对齐时间轴看,虽然麻烦点,但胜在稳定。

说说我现在的使用姿势

我现在每周五晚上,就打开我的小工具,拉一场经典比赛的数据,比如2001年大冠军杯中国对俄罗斯那场,赵蕊蕊的拦网数据,能让我研究半天,我把这些数据存成CSV文件,再用gonum做点简单的统计分析。说真的,用代码看比赛,你会看到很多直播转播里注意不到的细节,比如某个轮次卡轮的原因,或者某位队员的发球落点规律。

你看,我这篇文章写得跟我的代码似的,有点乱,还有一堆没用的注释,但这就是我真实折腾的过程。女排大冠军杯的回看,不光是看球,更是一种跟历史数据较劲的乐趣,你要是有兴趣,也可以拿Golang试试,从抓一场比赛的比分开始,慢慢你会陷进去的。

行了,不聊了,我得去修我的数据解析器了——2019年那届的页面,居然把技术统计藏在了JavaScript加载的弹窗里,害得我又得写个chromedp来模拟点击,回见。

用Golang写个女排大冠军杯回看小工具?这事儿我试过,真香