10 Goquery踩坑

// package
import "github.com/PuerkitoBio/goquery"

// go mod

require github.com/PuerkitoBio/goquery v1.5.1

API文档在pkg.go.dev这里,Github仓库在这里

goquery在Github上近9k的star,golang著名的爬虫框架colly也用的它。

goquery简介

goquery实现了与jQuery类似的功能,包括使用可链接语法操纵和查询HTML文档。

它为Go语言带来了一种类似于jQuery的语法和函数。它基于Go的net/html包和CSS Selector库cascadia。由于net/html解析器返回的是节点,而不是功能完整的DOM树,因此jQuery的有状态操作函数(例如height()css()detach())已被省去。

net/html解析器读取UTF-8编码的文件(Go默认处理的就是UTF-8编码的文件),所以要确保被操作的源文档是UTF-8编码的HTML文件。有关如何执行此操作的各种选项,可查看Github仓库的Wiki

在语法上,它尽可能接近jQuery,并在可能的情况下使用相同的方法名称,并且具有熟悉而类似的可链接接口。

jQuery是一个广受欢迎的库,因此,参照和遵循它的API来编写类似的HTML操作库会更好,这也是Go语言的精神(如fmt包的实现),即使jQuery有些方法看起来不那么直观(如index()等)。

注意:goquery以来net/html库,因此需要Go1.1+以上版本

根据方法的不同类型分类到不同的文件中,三个点(...)表示该方法可以重载(overloads)。

  • array.go(数组类位置选择操作):Eq()First()Get()Index...()Last()Slice()
  • expend.go(扩充选择的集合):Add...()AndSelf()Union()AddSelection()的别名
  • filter.go(过滤选择的集合):End()Filter...()Has...()Intersection()FilterSelection()的别名,Not...()
  • iteration.go(遍历节点):Each()EachWithBreak()Map()
  • manipulation.go(修改HTML):After...()Append...()Before...()Clone()Empty()Prepend...()Remove...()ReplaceWith...()Unwrap()Wrap...()WrapAll...()WrapInner...()
  • property.go(检查并获取节点属性值):Attr*(), RemoveAttr(), SetAttr()AddClass(), HasClass(), RemoveClass(), ToggleClass()Html()Length()Size()Length()的别名,Text()
  • query.go(判断节点身份):Contains()Is...()
  • traversal.go(遍历HTML文档树):,Children...()Contents()Find...()Next...()Parent[s]...()Prev...()Siblings...()
  • type.go(goquery公开的类型):DocumentSelectionMatcher
  • utilities.go(辅助函数,而不是* Selection的方法,这不是jQuery的一部分):NodeNameOuterHtml

了解了具体的功能和提供的函数,具体就是在调用上面函数的时候提供CSS选择器作为参数。

CSS选择器

分类类别描述语法例子
基本选择器通用选择器选择所有元素* `ns
基本选择器类型选择器按照给定的节点名称,选择所有匹配的元素elementnameinput 匹配任何 <input> 元素
基本选择器类选择器按照给定的 class 属性的值,选择所有匹配的元素.classname.index 匹配任何 class 属性中含有 "index" 的元素
基本选择器ID选择器按照 id 属性选择一个与之匹配的元素。需要注意的是,一个文档中,每个 ID 属性都应当是唯一的#idname#toc 匹配 ID 为 "toc" 的元素
基本选择器属性选择器按照给定的属性,选择所有匹配的元素[attr] [attr=value] [attr~=value] `[attr=value][attr^=value][attr$=value][attr*=value]`
分组选择器选择器列表,将不同的选择器组合在一起,它选择所有能被列表中的任意一个选择器选中的节点A,Bdiv, span 会同时匹配 <span> 元素和 <div> 元素
组合选择器后代组合器空格组合器选择前一个元素的后代节点A Bdiv span 匹配所有位于任意 <div> 元素之内的 <span> 元素
组合选择器直接子代组合器>组合器选择前一个元素的直接子代的节点A > Bul > li 匹配直接嵌套在 <ul> 元素内的所有 <li> 元素
组合选择器一般兄弟组合器~组合器选择兄弟元素,即后一个节点在前一个节点后面的任意位置,并且共享同一个父节点A ~ Bp ~ span 匹配同一父元素下,<p> 元素后的所有 <span> 元素
组合选择器紧邻兄弟组合器+组合器选择相邻元素,即后一个元素紧跟在前一个之后,并且共享同一个父节点A + Bh2 + p 会匹配所有紧邻在 <h2> 元素后的 <p> 元素
组合选择器列组合器``组合器选择属于某个表格行的节点
伪选择器伪类:伪选择器支持按照未被包含在文档树中的状态信息来选择元素a:visited 匹配所有曾被访问过的 <a> 元素
伪选择器伪元素::伪选择器用于表示无法用HTML语义表达的实体p::first-line 匹配所有 <p> 元素的第一行

goquery使用样例

大部分都和上面的一样,比较特殊的列在下面。

选择器说明
Find(“div[lang]")筛选含有lang属性的div元素
Find(“div[lang=zh]")筛选lang属性为zh的div元素
Find(“div[lang!=zh]")筛选lang属性不等于zh的div元素
Find(“div[lang¦=zh]")筛选lang属性为zh或者zh-开头的div元素
Find(“div[lang*=zh]")筛选lang属性包含zh这个字符串的div元素
Find(“div[lang~=zh]")筛选lang属性包含zh这个单词的div元素,单词以空格分开的
Find(“div[lang$=zh]")筛选lang属性以zh结尾的div元素,区分大小写
Find(“div[lang^=zh]")筛选lang属性以zh开头的div元素,区分大小写

下面的操作在选择器选出的内容中再进行过滤。

类别描述语法例子
内容过滤器筛选出的元素要包含指定的文本Find(":contains(text)")
  • Find("div:contains(DIV2)"),选择出的div元素要包含DIV2文本
  • Find(":empty),选出的元素都不能有子元素
  • Find("span:has(div)"),选出包含divspan元素,与has类似的contains
:first-child/:last-child选出其父元素的第一个子元素Find(":first-child")Find("div:first-child")
:first-of-type/:last-of-type选出其父元素的第一个该类型子元素Find(":first-of-type")Find("div:first-of-type")
:nth-child(n)/:nth-last-child(n)选出其父元素的第n个子元素Find(":nth-child(n)")Find("div:nth-child(3)")
:nth-of-type(n)/:nth-last-of-type(n)选出其父元素的第n个该类型子元素Find(":nth-of-type(n)")Find("div:nth-of-type(3)")
:only-child选出其父元素中只有该元素(数量唯一)的子元素Find(":only-child")Find("div:only-child")
:only-of-type选出其父元素中只有该类型元素(类型唯一)的子元素Find(":only-of-type")Find("div:only-of-type")
上次修改: 16 June 2020