这事儿得从一次深夜看球说起,那天我盯着NBA比赛直播,突然特想知道库里这赛季的真实命中率到底多少,可翻了半天官网,那些数据表格真是让人头大,我心想,既然自己是个写Golang的,干嘛不自己动手拉数据呢?说干就干,那咱们就用Golang写点东西,专门搞NBA数据,简称nbaqq——其实这个叫法是我自个儿瞎起的,NBA查询器”的意思。
为什么选Golang干这活儿
你可能会问,Python不香吗?确实香,但Golang有几个独特的地方让我觉得更顺手,比如它的并发模型,像goroutine和channel,处理大量请求时特别省心,你想想,NBA官方的API响应有时候快有时候慢,要是用串行方式一个一个请求,等得黄花菜都凉了。
还有一个重要的点,Golang编译完就是一个二进制文件,扔到服务器上直接跑,不用装什么依赖,实战的时候会把请求间隔控制好。
上代码,先看个简单的请求模板:
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func fetchPlayerStats(playerID string) {
url := fmt.Sprintf("https://stats.nba.com/stats/player/%s", playerID)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("User-Agent", "Mozilla/5.0")
req.Header.Set("Referer", "https://www.nba.com/")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("出错了:", err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
你看,就这么几行代码,就能把某个球员的数据拉下来,但要注意,NBA官网的API是有反爬机制的,User-Agent和Referer这两个头必须带,不然人家直接给你返回403。
数据解析那块怎么整
拿到的数据一般是JSON格式,Golang里解析JSON用标准库就够,比如你想拿到某场比赛的得分,数据嵌套得挺深,得一层一层剥开。
“JSON解析是nbaqq的核心环节”,因为NBA的数据结构实在复杂,比如说一场比赛的数据,顶级是resultSets,下面有header和rowSet,你得先解析出列名,再对应到每一行的数据,我习惯先定义一个结构体:
type GameStats struct {
Points float64 `json:"pts"`
Rebounds float64 `json:"reb"`
Assists float64 `json:"ast"`
}
但实际你会发现,NBA返回的JSON里字段名都是大写的,比如PTS、REB,这点得注意,写结构体标签时得对应好。
并发请求那点事儿
咱们聊聊核心,也就是goroutine怎么用,假设你想一次性查10个球员的数据,按顺序请求得花好几秒,但用并发可能一秒就完事。
用Golang的goroutine启动并发请求,配合sync.WaitGroup来等所有请求处理完,还得加上一个限流器,比如每秒只发5个请求,别把人家服务器打崩了。

下面是个并发的例子:
import (
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
players := []string{"curry", "james", "durant"}
for _, name := range players {
wg.Add(1)
go func(n string) {
defer wg.Done()
fetchPlayerStats(n)
time.Sleep(200 * time.Millisecond)
}(name)
}
wg.Wait()
fmt.Println("所有数据获取完毕")
}
用channel来传递数据也挺好使的,比如把所有结果发到一个channel里,最后统一处理,避免了共享内存的竞争条件。
处理数据时要注意的几个坑
第一个坑就是数据字段可能为空,比如某个新秀刚打了几场比赛,某些统计数据就是null,这时候解析时得做零值处理,否则float64会变成0,影响计算。
第二个坑是比赛时间格式,NBA用UTC时间,你得转成北京时间,Golang里面用time.Parse和time.LoadLocation来处理。
第三个坑是API权限,NBA的一些高级数据,比如球员追踪,需要特殊权限,普通的接口免费,但有些需要付费,自己写nbaqq的话,先用免费的接口足够了,比如/stats/commonplayerinfo之类的。
把数据存下来方便再用
解析完的数据,我一般存到SQLite或者简单的JSON文件里,用Golang操作SQLite很简单,如果写成JSON文件,直接序列化到文件里:
func saveToFile(data []byte, filename string) {
err := ioutil.WriteFile(filename, data, 0644)
if err != nil {
fmt.Println("保存失败:", err)
}
}
这样每次查询时先检查本地有没有缓存,没有再去请求API,省时省力。
关于可视化那点想法
数据拿到手之后,直接在终端打印表格也不错,用fmt.Printf格式化输出,或者用一些库把数据显示成表格,例如把球员得分、篮板、助攻整成一个简单的表格:
| 球员 | 得分 | 篮板 | 助攻 |
|---|---|---|---|
| 库里 | 7 | 1 | 3 |
| 詹姆斯 | 2 | 4 | 9 |
| 杜兰特 | 4 | 2 | 8 |
要是想做得更炫,可以结合前端生成图表,但那就超出今天聊的范围了。
写到最后想说的
其实写个nbaqq,一开始只是为了满足自己的好奇心,结果发现Golang做这事儿确实顺滑——编译快、并发强、部署简单,整个从数据抓取到解析再到存储的闭环,用Go写下来也就几百行代码,你可以拿它来查自己喜欢的球星,比如库里是不是又逆天了,或者看看咱们中国球员在NBA的表现。
我不打算写什么总结,反正数据就在那儿,代码也是现成的,你想玩就试试呗。
本文来自作者[kyadmin]投稿,不代表思利达立场,如若转载,请注明出处:http://cj.c-lida.com/post/84.html
评论列表(4条)
我是思利达的签约作者“kyadmin”!
希望本篇文章《用Golang写个NBA数据爬虫?这事儿真没那么玄乎》能对你有所帮助!
本站[思利达]内容主要涵盖:郑州思利达智能科技有限公司
本文概览:这事儿得从一次深夜看球说起,那天我盯着NBA比赛直播,突然特想知道库里这赛季的真实命中率到底多少,可翻了半天官网,那些数据表格真是让人头...