
在 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(`
Go
totally kicks
hacks
立即学习“前端免费学习笔记(深入)”;
`)
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)并避免因空格引发的静默匹配失败。











