导读:本期聚焦于布兰登创作的《如何在 Go 语言中使用 CSS 选择器解析 HTML 页面并提取指定元素值》,敬请观看详情。网络爬虫、数据采集、页面监控这些场景里,解析HTML并提取目标元素几乎是绕不开的一步。Go标准库自带的html包能解析DOM树,但要精准定位某个节点,写起来相当繁琐。goquery库把jQuery风格的选择器API带到了Go中,配合cascadia选择器引擎,可以用链式调用直接选中class、id、属性匹配的元素,再取出文本、属性或HTML片段。本文围绕goquery的安装、选择器语法、常见提取操作展开,覆盖Find、Each遍历、Attr取属性、Eq索引定位等核心方法,同时给出完整的抓取与解析示例,并附上正则辅助提取、编码处理和性能方面的注意事项,帮助你快速写出结构清晰又稳定的解析代码。

写爬虫或者做页面数据抽取时,最头疼的往往不是网络请求,而是拿到一大坨HTML之后怎么把想要的那几个值抠出来。用正则硬扛太脆弱,页面结构稍微变一下就全挂;用标准库 net/html 手动遍历DOM树又太啰嗦,找一个 class 都要写好几层判断。Go 社区里有个很成熟的方案叫 goquery,它把 jQuery 那套熟悉的选择器语法搬到了 Go 里,几行代码就能定位到目标元素,本文就围绕它详细讲讲怎么用。

如何在 Go 语言中使用 CSS 选择器解析 HTML 页面并提取指定元素值

goquery 是什么,为什么值得选

goquery 是基于 net/html 和 cascadia 两个库封装出来的解析工具。net/html 负责把HTML字符串解析成DOM树,cascadia 是一个纯Go实现的CSS选择器引擎,负责按选择器匹配节点,goquery 在这两者之上提供了类似 jQuery 的链式 API。也就是说,你在前端写惯了的 $(".article .title") 这种写法,在Go里几乎可以原样翻译过来。

安装非常简单,直接执行下面的命令即可:

go get github.com/PuerkitoBio/goquery

相比正则提取,goquery的优势在于容错性强:浏览器能正常渲染的HTML,net/html 基本都能解析,哪怕标签没闭合、属性带引号不带引号都能处理。相比手写DOM遍历,选择器表达意图更直接,代码量能少一大半。另外它是纯Go实现,没有CGO依赖,交叉编译到任何平台都没问题,这点对爬虫项目部署很友好。

基础用法:加载HTML并选中元素

先看一个最小的例子,从一段HTML字符串中提取所有链接。goquery 提供了 NewDocumentFromReaderNewDocumentFromResponse 等多种构造方式,最常用的还是从 io.Reader 加载:

package main

import (
    "fmt"
    "strings"

    "github.com/PuerkitoBio/goquery"
)

func main() {
    html := `<html>
        <body>
            <div class="list">
                <a href="https://ipipp.com/page1">第一页</a>
                <a href="https://ipipp.com/page2">第二页</a>
            </div>
        </body>
    </html>`

    doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
    if err != nil {
        panic(err)
    }

    // 用CSS选择器找到所有a标签,遍历并取出href和文本
    doc.Find(".list a").Each(func(i int, s *goquery.Selection) {
        href, exists := s.Attr("href")
        text := s.Text()
        if exists {
            fmt.Printf("链接%d: %s -> %s\n", i, text, href)
        }
    })
}

核心就是两个方法:Find 接收任意合法的CSS选择器,返回一个 Selection;Each 对选中的每个元素执行回调,回调参数里 s 代表当前元素,可以继续调用 s.Finds.Texts.Attr 等。这种嵌套能力在解析列表页时特别有用:先选中每一项的容器,再在容器内部找标题、价格、链接等字段。

常用选择器与提取技巧

goquery 支持 cascadia 的完整选择器语法,日常开发中高频的包括:#id 按 id 匹配,.class 按 class 匹配,a[href] 按属性存在匹配,a[href*="detail"] 按属性包含匹配,div > p 只匹配直接子元素,tr:nth-child(2) 按位置匹配。把这些组合起来基本能覆盖绝大多数页面的定位需求。

提取数据时常用的几个方法值得单独说一说。Text() 返回元素内的所有文本(含子孙节点拼接);Html() 返回元素内部HTML片段,需要保留结构时用它;Attr("name") 返回属性值和一个 bool 表示是否存在;Eq(index) 在选中集合里取第几个元素;First()Last() 顾名思义。下面这个例子演示解析一个表格:

doc.Find("table.data tbody tr").Each(func(i int, row *goquery.Selection) {
    name := row.Find("td").Eq(0).Text()
    price := row.Find("td").Eq(1).Text()
    link, _ := row.Find("td a").Attr("href")

    fmt.Printf("第%d行: 名称=%s, 价格=%s, 链接=%s\n", i+1, name, price, link)
})

有个细节容易被忽略:Text() 返回的是原始拼接文本,通常带有换行和大量空格,建议用 strings.TrimSpace 包一层,多个空白合并成一个可以再配合 regexp 处理,比如 regexp.MustCompile(`\s+`).ReplaceAllString(text, " "),数据会干净很多。

结合网络请求的完整实战

实际项目中一般是先发HTTP请求拿到响应体,再交给 goquery 解析。下面给出一个完整例子,抓取一个书籍列表页并输出结构化结果:

package main

import (
    "fmt"
    "log"
    "net/http"
    "regexp"
    "strings"

    "github.com/PuerkitoBio/goquery"
)

type Book struct {
    Title string
    Price string
}

var spaceRe = regexp.MustCompile(`\s+`)

func clean(s string) string {
    return strings.TrimSpace(spaceRe.ReplaceAllString(s, " "))
}

func main() {
    req, _ := http.NewRequest("GET", "https://ipipp.com/books", nil)
    req.Header.Set("User-Agent", "Mozilla/5.0")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        log.Fatalf("请求失败,状态码: %d", resp.StatusCode)
    }

    doc, err := goquery.NewDocumentFromResponse(resp)
    if err != nil {
        log.Fatal(err)
    }

    var books []Book
    doc.Find("ul.book-list li.item").Each(func(i int, s *goquery.Selection) {
        books = append(books, Book{
            Title: clean(s.Find("h3.title").Text()),
            Price: clean(s.Find("span.price").Text()),
        })
    })

    for _, b := range books {
        fmt.Printf("《%s》 售价: %s\n", b.Title, b.Price)
    }
}

这段代码里有两个工程上的好习惯:一是记得关闭响应体并检查状态码,避免连接泄漏;二是把文本清洗逻辑抽成独立函数,遍历逻辑保持简洁。如果目标站点是 GBK 编码(不少老站点仍是),直接解析会乱码,需要先用 golang.org/x/text/encoding/simplifiedchinese 把响应体转成 UTF-8 再喂给 goquery。

注意事项与性能建议

第一点,goquery 只解析静态HTML,页面里由 JavaScript 动态渲染的内容它拿不到。遇到这类站点,要么找接口直接请求数据,要么换 chromedp 这类无头浏览器方案,不要在 goquery 上死磕。第二点,选择器越精确越好,带结构层级的选择器比单纯的样式类名更不容易被页面改版误伤,因为样式类经常被前端构建工具改名。

性能方面,单个页面的解析开销通常远小于网络IO,一般不需要优化。但如果要批量解析上千个页面,注意每次 NewDocumentFromReader 都会完整建树,不要把同一个 body 重复解析多遍,应该解析一次后用多个选择器分别提取。另外 Each 里不要做耗时操作比如再发HTTP请求,会串行阻塞,需要并发时把数据收集好后丢给 goroutine 池处理更合理。

最后提一句调试技巧:选择器没匹配到任何元素时,goquery 不会报错,只是集合为空,Each 一次都不执行。排查时可以先打印 doc.Find(selector).Length() 确认命中数量,再逐步简化选择器定位是哪一段失效,这比盲目改代码效率高得多。掌握这些之后,用Go做页面数据提取基本就是流水线作业了。

Go语言CSS选择器goquery修改时间:2026-09-08 23:51:32

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260908/53055.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。