生活里的代码,代码里的篮球
我平时爱看NBA季后赛,也爱写点 Golang 的小工具,前几天跟朋友争论“谁是近十年季后赛得分王”,我俩谁也说服不了谁——我脑补数据,他凭感觉猜,后来我想,与其争,不如写个程序把数据拉下来算一算,今天把这过程拆开揉碎,写成一篇带点烟火气的文章,顺便聊聊 Golang 做数据抓取和统计的那些事儿。
为什么是 Golang ?不只是为了快
你可能觉得写个分析工具用 Python 更方便,确实,Python 库多,但我想用 Golang 的原因很简单:
- 编译成二进制,丢哪都能跑 —— 不需要装环境,给朋友发个 exe 他就能自己跑
- 并发简单到离谱 —— 抓多页数据时,
go关键字一把梭 - 标准库够硬 —— 连
net/http和encoding/json都内置,不用 pip 装一坨
作为一个 Go 玩家,写这种小项目简直像打季后赛一样——快速决策、高效执行,还有点爽。
第一步:搞清楚“数据场在哪”
想分析 NBA 季后赛得分王,得先知道数据源,常见的有:
- Basketball Reference —— 数据全,但页面结构偏老
- NBA 官方 API —— 接口规范,但需要 token
- 一些开源仓库(如
nba_api的 JS 版)
我选了 Basketball Reference,因为它 不需要认证,直接 GET 就能拿到 HTML 表格,虽然要解析 HTML,但 Golang 的 goquery 库(模仿 jQuery)能搞定。

第二步:用 Golang 抓取季后赛数据
先搞个简单的爬虫骨架,核心思路是:
- 构造历年季后赛数据的 URL
- 发送 HTTP 请求,拿到 HTML
- 用
goquery解析表格行 - 提取“球员名、球队、场均得分、总得分”这几个字段
package main
import (
"fmt"
"log"
"net/http"
"strings"
"github.com/PuerkitoBio/goquery"
)
func fetchPlayoffData(year int) []PlayerStat {
url := fmt.Sprintf("https://www.basketball-reference.com/playoffs/NBA_%d.html", year)
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// 解析表格
doc, _ := goquery.NewDocumentFromReader(resp.Body)
// 具体选择器要针对页面结构写,这里简化了
var players []PlayerStat
doc.Find("table#playoffs_players tbody tr").Each(func(i int, s *goquery.Selection) {
// 提取数据...
})
return players
}
踩坑小记:Basketball Reference 的表格 id 每年会变,
playoffs_players前面可能有年份后缀,我后来改成用 CSS 类名匹配,更稳。
第三步:计算得分王(不止一个维度)
既然要写“权威”文章,得分王不能只看总得分,还要看场均得分、效率值、关键比赛表现,我设计了三个指标:
| 指标 | 含义 | 计算方式 |
|---|---|---|
| 总得分王 | 整个季后赛累计得分最高 | sum(每场得分) |
| 场均得分王 | 至少打 10 场的球员场均最高 | 总得分 / 出场数 |
| 效率得分王 | 得分效率高且产量大 | 结合命中率和罚球得分 |
代码上,我用了 结构体 + 排序,Golang 的 sort.Slice 简直不要太顺手:
type PlayerStat struct {
Name string
Team string
TotalPts float64
Games int
AvgPts float64
Efficiency float64
}
// 按总得分排序
sort.Slice(players, func(i, j int) bool {
return players[i].TotalPts > players[j].TotalPts
})
第四步:用并发加速——像打季后赛一样疾风骤雨
季后赛数据每年都有,要是单线程从1980年拉到2024年,得等到花儿都谢了,Golang 的 goroutine 一出手:
func fetchAllYears(years []int) []PlayerStat {
ch := make(chan []PlayerStat)
for _, year := range years {
go func(y int) {
ch <- fetchPlayoffData(y)
}(year)
}
var allPlayers []PlayerStat
for range years {
allPlayers = append(allPlayers, <-ch...)
}
return allPlayers
}
这里有个小细节:go func(y int) 里必须传参,不能直接引用循环变量 year,不然所有 goroutine 读到的都是最后一个年份。这坑我掉过,扶着肩膀告诉你:闭包陷阱是 Go 新手的经典剧情。
第五步:输出结果(带点生活气息)
数据算好了,怎么呈现?我选了三种输出:
- 控制台表格:用
tabwriter对齐字段,看起来像命令行神器 - CSV 文件:方便导入 Excel 跟朋友对线
- JSON 文件:方便接前端或其他程序
打个比方,输出结果大概长这样:
排名 球员 球队 总得分 场均得分
1 LeBron James CLE/LAL 7893 28.4
2 Michael Jordan CHI 5987 33.5
3 Kareem Abdul-Jabbar LAL 5762 24.3
真实发现:如果算总得分,勒布朗确实遥遥领先(季后赛打得久);但算场均得分(至少20场),帮主依然恐怖——33.5分,比第二名高了快5分。
一些真实的“脏活”细节
写这个工具的时候,我遇到了几个实际问题,一点都不完美:
- 页面结构变化:Basketball Reference 偶尔改版,2023 年的表格选择器跟 2019 年不一样,我写了个 fallback 逻辑:先试新选择器,失败再用旧的。
- 反爬机制:连续发太多请求会被 ban,我加了随机延时和 User-Agent 轮换,代码里躺着一个
fakeUserAgents数组。 - 数据清洗:有些球员数据缺失(比如伤病退赛),我得用
if fields[3] != ""跳过空行。别小看这个 if,它拦住了三分之一的崩溃。 - 性能问题:并发拉 40 年数据,内存一度飙到 400MB,后来加了
sync.Pool复用字节缓冲区,降到 120MB。但我跟你说实话,不优化也行——反正就跑一次,谁在乎那300MB?
怎么用这个工具?(给身边的表弟示范)
我用一个叫 nba-score-king 的 Go 项目打包了所有代码,用法简单到离谱:
# 安装 go install github.com/yourname/nba-score-king@latest # 跑起来,默认输出1980-2024年的季后赛得分王 nba-score-king # 指定年份范围 nba-score-king -from 2000 -to 2024 -stat total # 输出CSV文件 nba-score-king -out csv -file result.csv
我表弟看完之后,自己去改了颜色输出,加了图表(用 termui 库),现在他每天在朋友圈晒“自定义统计图”——代码给别人用才是最爽的,因为你会看到它长出意料之外的枝条。
不是总结,只是停下来喘口气
写这篇文章的时候,我边写代码边听季后赛回放,偶尔站起来投个纸球,Golang 和篮球有一个共通点:规则简单,但细节无限,你永远可以在排序算法里发现新 bug,也永远能看到 0.1 秒内的绝杀。
如果你也想试试,直接从 GitHub 上拉我的仓库就行(自己去搜:nba-score-king),跑一遍,看看历史得分王是谁——然后你会发现,数据帮我们记住了很多凭感觉记不清的事。
代码和篮球一样,别光看,上手打一打。
本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://www.cdscds.cn/nba/464.html
评论列表(4条)
我是be365的签约作者“kyadmin”!
希望本篇文章《用 Golang 手撸一个NBA季后赛得分王分析工具》能对你有所帮助!
本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名
本文概览:生活里的代码,代码里的篮球我平时爱看NBA季后赛,也爱写点Golang的小工具,前几天跟朋友争论“谁是近十年季后赛得分王...