前两天看球,想查德甲积分榜,打开网易体育翻了半天,广告弹窗烦得不行,我就想,要不自己写个程序抓数据得了,反正我是个Golang爱好者,写个爬虫也就一杯咖啡的功夫。
为什么要自己搞?网易的页面太乱了
打开网易体育的德甲页面(https://sports.163.com/zb/),你会看到:
- 积分榜被夹在广告中间
- 数据更新有时候滞后
- 想快速比较球队数据得来回滑
与其忍,不如写个程序自动抓,Golang的net/http包和goquery库配合,十几行代码就能搞定。

第一步:分析网易德甲页面的结构
先打开网易体育的德甲页面,按F12看元素,你会发现积分榜表格的class是<table class="(>人<;)~zZc56c-06cd-09b7-4ac0 league-table">,里面每一行是一个球队:
<table class="(>人<;)~zZ06cd-09b7-4ac0-790e league-table">
<thead>...</thead>
<tbody>
<tr>
<td>1</td>
<td>拜仁慕尼黑</td>
<td>18</td>
<td>14</td>
<td>2</td>
<td>2</td>
<td>55</td>
<td>15</td>
<td>40</td>
<td>44</td>
</tr>
...
</tbody>
</table>
每一列对应:排名、球队、场次、胜、平、负、进球、失球、净胜球、积分。
第二步:写Golang代码,抓解析数据
下面这个程序用goquery解析页面,把数据塞进结构体里:
package main
import (
"fmt"
"log"
"net/http"
"strconv"
"strings"
"github.com/PuerkitoBio/goquery"
)
type Team struct {
Rank int
Name string
Played int
Won int
Drawn int
Lost int
GoalsFor int
GoalsAgainst int
GoalDiff int
Points int
}
func main() {
url := "https://sports.163.com/zb/"
resp, err := http.Get(url)
if err != nil {
log.Fatal("获取页面失败:", err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
log.Fatalf("状态码异常: %d %s", resp.StatusCode, resp.Status)
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
log.Fatal("解析文档失败:", err)
}
var teams []Team
doc.Find("table.league-table tbody tr").Each(func(i int, s *goquery.Selection) {
tds := s.Find("td")
if tds.Length() < 10 {
return
}
rank, _ := strconv.Atoi(strings.TrimSpace(tds.Eq(0).Text()))
name := strings.TrimSpace(tds.Eq(1).Text())
played, _ := strconv.Atoi(strings.TrimSpace(tds.Eq(2).Text()))
won, _ := strconv.Atoi(strings.TrimSpace(tds.Eq(3).Text()))
drawn, _ := strconv.Atoi(strings.TrimSpace(tds.Eq(4).Text()))
lost, _ := strconv.Atoi(strings.TrimSpace(tds.Eq(5).Text()))
goalsFor, _ := strconv.Atoi(strings.TrimSpace(tds.Eq(6).Text()))
goalsAgainst, _ := strconv.Atoi(strings.TrimSpace(tds.Eq(7).Text()))
goalDiff, _ := strconv.Atoi(strings.TrimSpace(tds.Eq(8).Text()))
points, _ := strconv.Atoi(strings.TrimSpace(tds.Eq(9).Text()))
teams = append(teams, Team{
Rank: rank,
Name: name,
Played: played,
Won: won,
Drawn: drawn,
Lost: lost,
GoalsFor: goalsFor,
GoalsAgainst: goalsAgainst,
GoalDiff: goalDiff,
Points: points,
})
})
// 打印表格
fmt.Println("德甲积分榜(数据来源:网易体育)")
fmt.Println(strings.Repeat("-", 80))
fmt.Printf("%-4s %-18s %-4s %-4s %-4s %-4s %-4s %-4s %-4s %-4s\n",
"排名", "球队", "场次", "胜", "平", "负", "进球", "失球", "净胜", "积分")
fmt.Println(strings.Repeat("-", 80))
for _, t := range teams {
fmt.Printf("%-4d %-18s %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d\n",
t.Rank, t.Name, t.Played, t.Won, t.Drawn, t.Lost,
t.GoalsFor, t.GoalsAgainst, t.GoalDiff, t.Points)
}
}
运行这个程序,终端会打印出类似这样的表格:
德甲积分榜(数据来源:网易体育)
--------------------------------------------------------------------------------
排名 球队 场次 胜 平 负 进球 失球 净胜 积分
--------------------------------------------------------------------------------
1 拜仁慕尼黑 18 14 2 2 55 15 40 44
2 多特蒙德 18 11 4 3 39 22 17 37
3 莱比锡红牛 18 10 5 3 35 20 15 35
...
第三步:把数据存起来,方便随时查
光打印不够,我一般存成JSON文件,这样随时可以用:
package main
import (
"encoding/json"
"os"
)
// Team 结构体同上
func main() {
// 抓取逻辑同上...
// 存为JSON
file, _ := os.Create("bundesliga.json")
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
encoder.Encode(teams)
fmt.Println("数据已保存至 bundesliga.json")
}
生成的JSON长这样:
[
{
"rank": 1,
"name": "拜仁慕尼黑",
"played": 18,
"won": 14,
"drawn": 2,
"lost": 2,
"goalsFor": 55,
"goalsAgainst": 15,
"goalDiff": 40,
"points": 44
},
...
]
第四步:搞个HTTP服务器,网页上实时看
既然都写到这了,干脆用net/http搭个迷你服务器,浏览器打开就能看:
package main
import (
"encoding/json"
"fmt"
"html/template"
"log"
"net/http"
"strconv"
"strings"
"sync"
"github.com/PuerkitoBio/goquery"
)
var (
teams []Team
mu sync.RWMutex
)
const tmpl = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">德甲积分榜 | 数据来源网易</title>
<style>
table { border-collapse: collapse; width: 100%; max-width: 800px; margin: 20px auto; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: center; }
th { background: #4CAF50; color: white; }
tr:nth-child(even) { background: #f2f2f2; }
tr:hover { background: #ddd; }
h1 { text-align: center; color: #333; }
.update { text-align: center; color: #666; margin-top: -10px; }
</style>
</head>
<body>
<h1>德甲积分榜</h1>
<p class="update">数据来源:网易体育 | 最后更新:{{.Time}}</p>
<table>
<thead>
<tr>
<th>排名</th><th>球队</th><th>场次</th><th>胜</th><th>平</th><th>负</th>
<th>进球</th><th>失球</th><th>净胜</th><th>积分</th>
</tr>
</thead>
<tbody>
{{range .Teams}}
<tr>
<td>{{.Rank}}</td>
<td><strong>{{.Name}}</strong></td>
<td>{{.Played}}</td>
<td>{{.Won}}</td>
<td>{{.Drawn}}</td>
<td>{{.Lost}}</td>
<td>{{.GoalsFor}}</td>
<td>{{.GoalsAgainst}}</td>
<td>{{.GoalDiff}}</td>
<td><strong>{{.Points}}</strong></td>
</tr>
{{end}}
</tbody>
</table>
</body>
</html>
`
func fetchData() {
url := "https://sports.163.com/zb/"
resp, _ := http.Get(url)
if resp == nil || resp.StatusCode != 200 {
return
}
defer resp.Body.Close()
doc, _ := goquery.NewDocumentFromReader(resp.Body)
var newTeams []Team
doc.Find("table.league-table tbody tr").Each(func(i int, s *goquery.Selection) {
tds := s.Find("td")
if tds.Length() < 10 {
return
}
t := Team{
Rank: atoi(tds.Eq(0).Text()),
Name: strings.TrimSpace(tds.Eq(1).Text()),
Played: atoi(tds.Eq(2).Text()),
Won: atoi(tds.Eq(3).Text()),
Drawn: atoi(tds.Eq(4).Text()),
Lost: atoi(tds.Eq(5).Text()),
GoalsFor: atoi(tds.Eq(6).Text()),
GoalsAgainst: atoi(tds.Eq(7).Text()),
GoalDiff: atoi(tds.Eq(8).Text()),
Points: atoi(tds.Eq(9).Text()),
}
newTeams = append(newTeams, t)
})
if len(newTeams) > 0 {
mu.Lock()
teams = newTeams
mu.Unlock()
}
}
func atoi(s string) int {
i, _ := strconv.Atoi(strings.TrimSpace(s))
return i
}
func handler(w http.ResponseWriter, r *http.Request) {
mu.RLock()
defer mu.RUnlock()
t := template.Must(template.New("page").Parse(tmpl))
data := struct {
Teams []Team
Time string
}{
Teams: teams,
Time: "实时数据",
}
t.Execute(w, data)
}
func main() {
// 启动时先抓一次
fetchData()
// 定时抓取(比如每5分钟)
go func() {
// 实际用time.Ticker
}()
http.HandleFunc("/", handler)
fmt.Println("访问 http://localhost:8080 查看德甲积分榜")
log.Fatal(http.ListenAndServe(":8080", nil))
}
现在打开http://localhost:8080,一个清晰无广告的德甲积分榜就出来了。
第五步:加点实用小功能
既然都写到这了,顺手加了几个实用功能:
- 命令行查询:
go run . --team 拜仁直接输出拜仁数据 - 球队比较:
go run . --compare 拜仁,多特对比两个球队 - 降级区高亮:积分榜最后三名自动标红
代码不贴了,原理都一样,就是多写几个if判断。
实际跑起来的效果
我设了个定时任务,每15分钟跑一次,数据基本和网易体育同步,偶尔网易改页面结构会抓不到,这时候加个容错就行——抓不到就用上次的数据,反正积分榜变化没那么快。
说实话,这东西搞完后我反而更频繁看积分榜了,以前嫌网易广告多懒得翻,现在终端里敲个命令就出来,或者打开浏览器书签,干净利落。
哦对,有一次拜仁输球,我程序跑出来显示“拜仁慕尼黑 排名2”,我盯着屏幕看了三秒,确认不是程序bug,才接受现实——拜仁也有翻车的时候啊。
最后
整个项目代码不到100行,Github上都有,如果你也烦网易体育的广告,不如花半小时写一个自己用的,反正Golang写爬虫是真顺手,goquery解析HTML跟jQuery似的,干就完了。
代码放这了,想用直接复制,对了,网易如果改页面结构,你记得把table.league-table这个选择器改一下就行,反正程序员嘛,哪里有bug改哪里。
本文来自作者[kyadmin]投稿,不代表678体育 - 全网热门体育赛事高清直播平台立场,如若转载,请注明出处:http://www.yuanlikao.com/jk/1849.html
评论列表(4条)
我是678体育 - 全网热门体育赛事高清直播平台的签约作者“kyadmin”!
希望本篇文章《用Golang写一个德甲积分榜爬虫,从网易体育抓数据,自己搞个实时排名》能对你有所帮助!
本站[678体育 - 全网热门体育赛事高清直播平台]内容主要涵盖:678体育,678体育官网,678赛事直播
本文概览:前两天看球,想查德甲积分榜,打开网易体育翻了半天,广告弹窗烦得不行,我就想,要不自己写个程序抓数据得了,反正我是个Golang爱好者,写...