用Golang写一个德甲积分榜爬虫,从网易体育抓数据,自己搞个实时排名

前两天看球,想查德甲积分榜,打开网易体育翻了半天,广告弹窗烦得不行,我就想,要不自己写个程序抓数据得了,反正我是个Golang爱好者,写...

前两天看球,想查德甲积分榜,打开网易体育翻了半天,广告弹窗烦得不行,我就想,要不自己写个程序抓数据得了,反正我是个Golang爱好者,写个爬虫也就一杯咖啡的功夫。

为什么要自己搞?网易的页面太乱了

打开网易体育的德甲页面(https://sports.163.com/zb/),你会看到:

  • 积分榜被夹在广告中间
  • 数据更新有时候滞后
  • 想快速比较球队数据得来回滑

与其忍,不如写个程序自动抓,Golang的net/http包和goquery库配合,十几行代码就能搞定。

用Golang写一个德甲积分榜爬虫,从网易体育抓数据,自己搞个实时排名

第一步:分析网易德甲页面的结构

先打开网易体育的德甲页面,按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)

文章推荐

发表回复

本站作者才能评论

评论列表(4条)

  • kyadmin
    kyadmin 2026-07-17

    我是678体育 - 全网热门体育赛事高清直播平台的签约作者“kyadmin”!

  • kyadmin
    kyadmin 2026-07-17

    希望本篇文章《用Golang写一个德甲积分榜爬虫,从网易体育抓数据,自己搞个实时排名》能对你有所帮助!

  • kyadmin
    kyadmin 2026-07-17

    本站[678体育 - 全网热门体育赛事高清直播平台]内容主要涵盖:678体育,678体育官网,678赛事直播

  • kyadmin
    kyadmin 2026-07-17

    本文概览:前两天看球,想查德甲积分榜,打开网易体育翻了半天,广告弹窗烦得不行,我就想,要不自己写个程序抓数据得了,反正我是个Golang爱好者,写...

    联系我们

    工作时间:周一至周五,9:30-18:30,节假日休息

    关注我们