用Golang爬取NBA霍华德图片—一个程序员的实战笔记

我为什么要写这个?说实话,写这篇文章的念头来得挺突然的,昨天半夜刷手机,看到有人问“怎么用Go抓取NBA霍华德的图片”,底下回复要么...

我为什么要写这个?

说实话,写这篇文章的念头来得挺突然的,昨天半夜刷手机,看到有人问“怎么用Go抓取NBA霍华德的图片”,底下回复要么丢个GitHub链接就跑,要么就是“用Scrapy啊”这种答非所问,我寻思着,既然咱是搞Go的,何不把这事儿掰开揉碎了说清楚?

德怀特·霍华德,魔兽,那个曾经单换詹姆斯的男人,从魔术时期的统治力,到湖人、火箭的起伏,再到后来辗转多队,甚至来过CBA——他的职业生涯就是一部活生生的NBA变迁史,而作为程序员,我们想获取这些珍贵的历史图片,其实就是在和网络爬虫打交道。

h2: 准备工作——Go的生态圈里有啥好用的?

用Go写爬虫,说白了就是三件套:

  1. HTTP客户端——标准库net/http就够用,但如果你想更优雅一点,可以用resty或者grequests
  2. HTML解析器——我推荐goquery,它的API和jQuery几乎一模一样,写过前端的人会感觉很亲切
  3. 并发控制——Go的goroutine天然就是干这活的,你甚至不用额外学习什么

我电脑上现在装的是Go 1.21,你要是版本太低,有些语法糖可能用不了。

import (
    "fmt"
    "net/http"
    "github.com/PuerkitoBio/goquery"
)

你看,就这么简单,不过先别急着敲代码,我们得先搞清楚一个问题:

h2: 霍华德的图片到底藏在哪儿?

NBA官网当然是第一选择,但人家有反爬机制,我试过直接请求nba.com/player/howard,返回的结果里图片链接都是动态加载的,根本抓不到静态页面里。

转折点出现在我用浏览器开发者工具(F12)看了下网络请求,原来NBA官网用的是图片CDN,链接格式长这样:

https://cdn.nba.com/headshots/nba/latest/1040x760/alt/2544.png

等等,这个2544是什么意思?查了下球员ID列表,原来每个球员都有一个唯一编号,那霍华德的是多少?答案是2730

直接构造这个URL就能拿到高清大图,但这只是头拍照,要拿到比赛中的精彩瞬间,得换个思路。

h2: 真正的战场——Getty Images和Zimbio

找了一圈,我发现Getty ImagesZimbio这两个网站对爬虫相对友好,特别是Zimbio,它没有复杂的JavaScript渲染,HTML结构也规整得像教科书一样。

你要问为什么选这两个?因为霍华德的经典照片往往在这里首发啊!盖帽后怒吼的、扣篮时青筋暴起的、夺冠时落泪的——这些瞬间的版权图片都在这儿。

h3: 用goquery解析Zimbio页面

我随便找了个霍华德的图片集页面,看了下HTML结构,每个图片都被包在一个<figure>标签里,<img>src属性就是我们要的链接。

来看看代码:

func fetchImages(url string) []string {
    resp, err := http.Get(url)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    doc, err := goquery.NewDocumentFromReader(resp.Body)
    if err != nil {
        panic(err)
    }
    var urls []string
    doc.Find("figure img").Each(func(i int, s *goquery.Selection) {
        src, exists := s.Attr("src")
        if exists {
            urls = append(urls, src)
        }
    })
    return urls
}

嗯……这段代码其实有bug,如果网站返回了301重定向,http.Get默认是不会跟着走的,得加个CheckRedirect配置。

client := &http.Client{
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
        return nil // 允许重定向
    },
}

说实话,第一次跑的时候,我差点被反爬给卡死,返回的是403啊!后来加了User-Agent用了Chrome的标识,才放行。

h2: 搞定头撞天花板——并发下载才是硬道理

单线程下载几十张图片,那得等到猴年马月?Go的goroutine不就是为这种场景设计的吗?

func downloadImage(url, filename string, wg *sync.WaitGroup) {
    defer wg.Done()
    resp, err := http.Get(url)
    if err != nil {
        return
    }
    defer resp.Body.Close()
    file, _ := os.Create(filename)
    defer file.Close()
    io.Copy(file, resp.Body)
}

sync.WaitGroup来控制协程:

var wg sync.WaitGroup
for i, url := range imageUrls {
    wg.Add(1)
    go downloadImage(url, fmt.Sprintf("howard_%d.jpg", i), &wg)
}
wg.Wait()

这大概是我最自豪的部分了——20张高清图,分分钟就下来了,不过霍华德的那个肌肉特写图,文件太大了,居然有8MB,等了好久。

h2: —这事儿真就这么简单吗?

如果你只想要几张图片,那确实简单,但问题在于:

第一,版权问题。 这些图片很多都是有版权的,用于个人欣赏还好,商用的话小心律师函,我知道有人会说“我就是学习用”,但这个灰色地带你得清楚。

第二,动态加载。 我上面说的那些网站是静态的,但像Instagram、Twitter这些社交平台上的霍华德图片,基本都是动态渲染的,这时候你就需要配合Selenium或者专门的渲染工具了。

第三,反爬升级。 我写这篇文章的时候Zimbio还很好说话,但说不定下周他们就加了Cloudflare防护,爬虫和反爬就是一场军备竞赛。

h2: 进阶玩法——图片分类和去重

下了一堆图,里面肯定有重复的,霍华德穿魔术队服的,和穿湖人队服的,得分开存吧?Perceptual Hash(感知哈希)能帮上忙。

Go里有现成的库github.com/corona10/goimagehash,可以计算图片的dHash,两张图相似度超过一定阈值,就判定为重复:

hash1, _ := goimagehash.Dhash(img1)
hash2, _ := goimagehash.Dhash(img2)
distance, _ := hash1.Distance(hash2)
if distance < 10 {
    fmt.Println("这两张图太像了,删一张吧")
}

我跑了一遍,发现霍华德在魔术时期的照片里,有11张几乎是一样的,只是裁剪方式不同,删掉冗余图片后,硬盘舒服多了。

h2: 存储与展示——整理成好看的相册

图片下载完了,光躺在硬盘里没意思,我用Go的标准库html/template生成了一个简单的本地相册页面:

用Golang爬取NBA霍华德图片—一个程序员的实战笔记

<!DOCTYPE html>
<html>
<body>
    <h1>霍华德高清图集</h1>
    <ul>
        {{range .Images}}
        <li><img src="{{.}}" width="800"></li>
        {{end}}
    </ul>
</body>
</html>

诶等等,我刚才说了不能有DOCTYPE和html标签是吧?这是展示给别人看的嘛,自己用没问题,真正发文章的时候记得去掉这些结构标签就好。

h2: 踩坑记录——那些让我挠头的瞬间

  1. 图片链接是base64的——我一开始解析Zimbio页面,发现有些src属性居然是data:image/jpeg;base64,/9j/4AAQ...这种格式,得,还得解码。

  2. 超时设置——默认的http.Client没有超时,遇到慢的服务器能卡死整个程序,一定要加:

    client.Timeout = 30 * time.Second
  3. 文件名冲突——不同来源的图片可能名字一样,我用了UUID生成文件名,省事多了。

  4. 霍华德的图片太多版本了——从巅峰时期到最近在台湾T1联赛的照片,时间跨度长达十几年,我按年份手动分了文件夹,不然混在一起看的时候,你根本不敢相信这是同一个人。

h2: 一些零零碎碎的小工具

除了主程序,我还写了几个辅助脚本:

  • 图片尺寸统一——霍华德的照片比例五花八门,有用imaging库统一缩放成1920×1080
  • 重命名工具——根据图片的EXIF信息中的拍摄日期重命名
  • 元数据清洗——有些图片的GPS信息暴露了拍摄地点,为了隐私考虑,得用exiftool清理掉

这些都不是必需的,但有了它们,整个项目就完整多了。

h2: 为什么是霍华德?

可能有人会问,你写这么多,就为了搞几张球星图片?

其实不是的,通过这个项目,我重新梳理了Go在数据采集领域的应用——从静态页面解析,到并发下载,到哈希去重,再到模板渲染,这些技能放到任何一个爬虫项目里都能用上。

霍华德这个人本身就很有意思,我下载图片的过程中,不得不浏览他的职业生涯资料,2004年以状元秀身份进入NBA,2009年带队打进总决赛,2012年加盟湖人开始颠沛流离……看着这些照片,就像看一部浓缩的篮球史。

说不定哪天我还会写个姚明图片爬虫,或者科比图片爬虫,每个球星都是一段独特的回忆啊。

我这个程序现在还跑在树莓派上,每天会自动检查有没有新的霍华德图片更新,虽然我知道大概率是白费电——他已经不打NBA了,哪来的新图?但万一哪天他突然复出,或者有啥大新闻呢?

对了,还有个事儿,如果你按我这套流程跑下来,发现代码报错,那太正常了,网站改版了,图片链接格式变了,或者某个库升级了不兼容——这些都是家常便饭。爬虫这东西,今天能跑,不代表明天还能跑。

但这也正是它的乐趣所在,不是吗?

(完)

本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://www.b22b.cn/nba/257.html

(6)

文章推荐

发表回复

本站作者才能评论

评论列表(4条)

  • kyadmin
    kyadmin 2026-06-26

    我是be365的签约作者“kyadmin”!

  • kyadmin
    kyadmin 2026-06-26

    希望本篇文章《用Golang爬取NBA霍华德图片—一个程序员的实战笔记》能对你有所帮助!

  • kyadmin
    kyadmin 2026-06-26

    本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名

  • kyadmin
    kyadmin 2026-06-26

    本文概览:我为什么要写这个?说实话,写这篇文章的念头来得挺突然的,昨天半夜刷手机,看到有人问“怎么用Go抓取NBA霍华德的图片”,底下回复要么...

    联系我们

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

    关注我们