
在 goquery 中,`class="text title"` 表示元素同时拥有 `text` 和 `title` 两个独立类名,需用 `.text.title`(无空格、连续点号)语法匹配,而非 `.text title`(后者为后代选择器)。理解 html class 属性的多值语义是正确使用 css 选择器的关键。
HTML 规范中,class 属性是一个以空格分隔的类名列表,而非单一字符串。因此 实际表示该元素同时属于 text 类和 title 类。goquery 基于标准 CSS 选择器语法解析,其行为与浏览器 DevTools 中的 document.querySelectorAll() 完全一致:
- ✅ .text.title:匹配同时具有 text 和 title 两个类的元素(交集)
- ❌ .text title:匹配在 text 类元素内部的 title 类元素(后代选择器,非目标意图)
- ✅ .title:匹配任意带有 title 类的元素(无论是否还有其他类)
- ✅ .text:匹配任意带有 text 类的元素
以下为完整可运行示例,演示正确用法及常见误区:
package main
import (
"fmt"
"strings"
"github.com/PuerkitoBio/goquery"
)
func main() {
html := strings.NewReader(`
<html>
<body>
<h1><span class="text title">Go </span></h1>
<p><span class="text">totally </span><span class="post">kicks </span></p>
<p><span class="text">hacks </span></p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/ai/1801" title="万兴爱画"><img
src="https://img.php.cn/upload/ai_manual/000/000/000/175680369151191.png" alt="万兴爱画" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/ai/1801" title="万兴爱画">万兴爱画</a>
<p>万兴爱画AI绘画生成工具</p>
</div>
<a href="/ai/1801" title="万兴爱画" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p>
</body>
</html>`)
doc, _ := goquery.NewDocumentFromReader(html)
// ✅ 正确:查找同时拥有 "text" 和 "title" 类的元素
fmt.Println("=== .text.title ===")
doc.Find(".text.title").Each(func(i int, s *goquery.Selection) {
class, _ := s.Attr("class")
fmt.Printf("class=%q, text=%q\n", class, s.Text())
// 输出:class="text title", text="Go "
})
// ✅ 正确:仅查找含 "title" 类的元素(宽松匹配)
fmt.Println("\n=== .title ===")
doc.Find(".title").Each(func(i int, s *goquery.Selection) {
class, _ := s.Attr("class")
fmt.Printf("class=%q, text=%q\n", class, s.Text())
// 输出:class="text title", text="Go "
})
// ❌ 错误:".text title" 会查找 .text 元素内部的 .title 元素(本例中无匹配)
fmt.Println("\n=== .text title (后代选择器,无结果) ===")
doc.Find(".text title").Each(func(i int, s *goquery.Selection) {
fmt.Println("UNEXPECTED MATCH:", s.Text())
})
}⚠️ 注意事项:
- goquery.Find() 不支持正则或模糊匹配(如 class*="text"),如需按子字符串筛选类名,需结合 Filter() 手动判断:
doc.Find("*").Filter(func(i int, s *goquery.Selection) bool { class, exists := s.Attr("class") return exists && strings.Contains(class, "text") }) - 多类选择器顺序无关(.title.text 与 .text.title 等效);
- 类名区分大小写,且不可包含空格、点号或括号等 CSS 特殊字符(应通过 HTML 实体或数据属性替代)。
掌握这一基本原理后,你就能准确构造复杂选择器(如 .btn.btn-primary.disabled)并避免因空格引发的静默匹配失败。










