golang goquery selector(选择器) 示例大全

本文为原创文章,转载注明出处,欢迎扫码关注公众号flysnow_org或者网站www.flysnow.org/,第一时间看后续精彩文章。以为好的话,顺手分享到朋友圈吧,感谢支持。html

最近研究Go爬虫相关的知识,使用到goquery这个库比较多,尤为是对爬取到的HTML进行选择和查找匹配的内容时,goquery的选择器使用尤为多,并且还有不少不经常使用但又颇有用的选择器,这里总结下,以供参考。前端

若是你们之前作过前端开发,对jquery不会陌生,goquery相似jquery,它是jquery的go版本实现。使用它,能够很方便的对HTML进行处理。jquery

基于HTML Element 元素的选择器

这个比较简单,就是基于a,p等这些HTML的基本元素进行选择,这种直接使用Element名称做为选择器便可。好比dom.Find("div")dom

func main() {
	html := `<body> <div>DIV1</div> <div>DIV2</div> <span>SPAN</span> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find("div").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
}
复制代码

以上示例,能够把div元素筛选出来,而body,span并不会被筛选。测试

ID 选择器

这个是使用频次最多的,相似于上面的例子,有两个div元素,其实咱们只须要其中的一个,那么咱们只须要给这个标记一个惟一的id便可,这样咱们就可使用id选择器,精肯定位了。网站

func main() {
	html := `<body> <div id="div1">DIV1</div> <div>DIV2</div> <span>SPAN</span> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find("#div1").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
}
复制代码

Element ID 选择器

id选择器以#开头,紧跟着元素id的值,使用语法为dom.Find(#id),后面的例子我会简写为Find(#id),你们知道这是表明goquery选择器的便可。spa

若是有相同的ID,可是它们又分别属于不一样的HTML元素怎么办?有好办法,和Element结合起来。好比咱们筛选元素为div,而且iddiv1的元素,就可使用Find(div#div1)这样的筛选器进行筛选。code

因此这类筛选器的语法为Find(element#id),这是经常使用的组合方法,好比后面讲的过滤器也能够采用这种方式组合使用。cdn

Class选择器

class也是HTML中经常使用的属性,咱们能够经过class选择器来快速的筛选须要的HTML元素,它的用法和ID选择器相似,为Find(".class")htm

func main() {
	html := `<body> <div id="div1">DIV1</div> <div class="name">DIV2</div> <span>SPAN</span> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find(".name").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
}
复制代码

以上示例中,就筛选出来classname的这个div元素。

Element Class 选择器

class选择器和id选择器同样,也能够结合着HTML元素使用,他们的语法也相似Find(element.class),这样就能够筛选特定element、而且指定class的元素。

属性选择器

一个HTML元素都有本身的属性以及属性值,因此咱们也能够经过属性和值筛选元素。

func main() {
	html := `<body> <div>DIV1</div> <div class="name">DIV2</div> <span>SPAN</span> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find("div[class]").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
}
复制代码

示例中咱们经过div[class]这个选择器,筛选出Element为div而且有class这个属性的,因此第一个div没有被筛选到。

刚刚上面这个示例是采用是否存在某个属性为筛选器,同理,咱们能够筛选出属性为某个值的元素。

dom.Find("div[class=name]").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
复制代码

这样咱们就能够筛选出class这个属性值为namediv元素。

固然咱们这里以class属性为例,还能够用其余属性,好比href等不少,自定义属性也是能够的。

除了彻底相等,还有其余匹配方式,使用方式相似,这里统一列举下,再也不举例

选择器 说明
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("div[id][lang=zh]"),用多个中括号连起来便可。当有多个属性筛选器的时候,要同时知足这些筛选器的元素才能被筛选出来。

parent>child选择器

若是咱们想筛选出某个元素下符合条件的子元素,咱们就可使用子元素筛选器,它的语法为Find("parent>child"),表示筛选parent这个父元素下,符合child这个条件的最直接(一级)的子元素。

func main() {
	html := `<body> <div lang="ZH">DIV1</div> <div lang="zh-cn">DIV2</div> <div lang="en">DIV3</div> <span> <div>DIV4</div> </span> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find("body>div").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
}
复制代码

以上示例,筛选出body这个父元素下,符合条件的最直接的子元素div,结果是DIV一、DIV二、DIV3,虽然DIV4也是body的子元素,但不是一级的,因此不会被筛选到。

那么问题来了,我就是想把DIV4也筛选出来怎么办?就是要筛选body下全部的div元素,不论是一级、二级仍是N级。有办法的,goquery考虑到了,只须要把大于号(>)改成空格就行了。好比上面的例子,改成以下选择器便可。

dom.Find("body div").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
复制代码

prev+next相邻选择器

假设咱们要筛选的元素没有规律,可是该元素的上一个元素有规律,咱们就可使用这种下一个相邻选择器来进行选择。

func main() {
	html := `<body> <div lang="zh">DIV1</div> <p>P1</p> <div lang="zh-cn">DIV2</div> <div lang="en">DIV3</div> <span> <div>DIV4</div> </span> <p>P2</p> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find("div[lang=zh]+p").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
}
复制代码

这个示例演示了这种用法,咱们想选择<p>P1</p>这个元素,可是没啥规律,咱们发现它前面的<div lang="zh">DIV1</div>颇有规律,能够选择,因此咱们就能够采用Find("div[lang=zh]+p")达到选择P元素的目的。

这种选择器的语法是("prev+next"),中间是一个加号(+),+号先后也是选择器。

本文为原创文章,转载注明出处,欢迎扫码关注公众号flysnow_org或者网站www.flysnow.org/,第一时间看后续精彩文章。一些比较可耻的网站抓取个人文章会去掉版权信息,这里再写一段,你们见谅。

prev~next选择器

有相邻就有兄弟,兄弟选择器就不必定要求相邻了,只要他们共有一个父元素就能够。

dom.Find("div[lang=zh]~p").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
复制代码

刚刚的例子,只须要把+号换成~号,就能够把P2也筛选出来,由于P2P1DIV1都是兄弟。

兄弟选择器的语法是("prev~next"),也就是相邻选择器的+换成了~

内容过滤器

有时候咱们使用选择器选择出来后后,但愿再过滤一下,这时候就用到过滤器了,过滤器有不少,咱们先讲内容过滤器这一种。

dom.Find("div:contains(DIV2)").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
复制代码

Find(":contains(text)")表示筛选出的元素要包含指定的文本,咱们例子中要求选择出的div元素要包含DIV2文本,那么只有一个DIV2元素知足要求。

此外还有Find(":empty")表示筛选出的元素都不能有子元素(包括文本元素),只筛选那些不包含任何子元素的元素。

Find(":has(selector)")contains差很少,只不过这个是包含的是元素节点。

dom.Find("span:has(div)").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Text())
	})
复制代码

以上示例表示筛选出包含div元素的span节点。

:first-child过滤器

:first-child过滤器,语法为Find(":first-child"),表示筛选出的元素要是他们的父元素的第一个子元素,若是不是,则不会被筛选出来。

func main() {
	html := `<body> <div lang="zh">DIV1</div> <p>P1</p> <div lang="zh-cn">DIV2</div> <div lang="en">DIV3</div> <span> <div style="display:none;">DIV4</div> <div>DIV5</div> </span> <p>P2</p> <div></div> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find("div:first-child").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Html())
	})
}
复制代码

以上例子中,咱们使用Find("div")会筛选出全部的div元素,可是咱们加了:first-child后,就只有DIV1DIV4了,由于只有这两个是他们父元素的第一个子元素,其余的DIV都不知足。

:first-of-type过滤器

:first-child选择器限制的比较死,必须得是第一个子元素,若是该元素前有其余在前面,就不能用:first-child了,这时候:first-of-type就派上用场了,它要求只要是这个类型的第一个就能够,咱们把上面的例子微调下。

func main() {
	html := `<body> <div lang="zh">DIV1</div> <p>P1</p> <div lang="zh-cn">DIV2</div> <div lang="en">DIV3</div> <span> <p>P2</p> <div>DIV5</div> </span> <div></div> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find("div:first-of-type").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Html())
	})
}
复制代码

改动很简单,把原来的DIV4换成了P2,若是咱们还使用:first-child,DIV5是不能被筛选出来的,由于它不是第一个子元素,它前面还有一个P2。这时候咱们使用:first-of-type就能够达到目的,由于它要求是同类型第一个就能够。DIV5就是这个div类型的第一个元素,P2不是div类型,被忽略。

:last-child 和 :last-of-type过滤器

这两个正好和上面的:first-child:first-of-type相反,表示最后一个,这里再也不举例,你们能够本身试试。

:nth-child(n) 过滤器

这个表示筛选出的元素是其父元素的第n个元素,n以1开始。因此咱们能够知道:first-child:nth-child(1)是相等的。经过指定n,咱们就很灵活的筛选出咱们须要的元素。

func main() {
	html := `<body> <div lang="zh">DIV1</div> <p>P1</p> <div lang="zh-cn">DIV2</div> <div lang="en">DIV3</div> <span> <p>P2</p> <div>DIV5</div> </span> <div></div> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find("div:nth-child(3)").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Html())
	})
}
复制代码

这个示例会筛选出DIV2,由于DIV2是其父元素body的第三个子元素。

:nth-of-type(n) 过滤器

:nth-of-type(n):nth-child(n) 相似,只不过它表示的是同类型元素的第n个,因此:nth-of-type(1):first-of-type是相等的,你们能够本身试试,这里再也不举例。

nth-last-child(n) 和:nth-last-of-type(n) 过滤器

这两个和上面的相似,只不过是倒序开始计算的,最后一个元素被当成了第一个。你们本身测试下看看效果,很明显。

:only-child 过滤器

Find(":only-child") 过滤器,从字面上看,能够猜想出来,它表示筛选的元素,在其父元素中,只有它本身,它的父元素没有其余子元素,才会被匹配筛选出来。

func main() {
	html := `<body> <div lang="zh">DIV1</div> <span> <div>DIV5</div> </span> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find("div:only-child").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Html())
	})
}
复制代码

示例中DIV5就能够被筛选出来,由于它是它的父元素span达到惟一子元素,但DIV1就不是,因此不能呗筛选出来。

:only-of-type 过滤器

上面的例子,若是想筛选出DIV1怎么办?可使用Find(":only-of-type"),由于它是它的父元素中,惟一的div元素,这就是:only-of-type过滤器所要作的,同类型元素只要只有一个,就能够被筛选出来。你们把上面的例子改为:only-of-type试试,看看是否有DIV1

选择器或(|)运算

若是咱们想同时筛选出div,span等元素怎么办?这时候能够采用多个选择器进行组合使用,而且以逗号(,)分割,Find("selector1, selector2, selectorN")表示,只要知足其中一个选择器就能够被筛选出来,也就是选择器的或(|)运算操做。

func main() {
	html := `<body> <div lang="zh">DIV1</div> <span> <div>DIV5</div> </span> </body> `

	dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html))
	if err!=nil{
		log.Fatalln(err)
	}

	dom.Find("div,span").Each(func(i int, selection *goquery.Selection) {
		fmt.Println(selection.Html())
	})
}
复制代码

小结

goquery 是解析HTML网页必备的利器,在爬虫抓取网页的过程当中,灵活的使用goquery不一样的选择器,可让咱们的抓取工做事半功倍,大大提高爬虫的效率。

本文为原创文章,转载注明出处,欢迎扫码关注公众号flysnow_org或者网站www.flysnow.org/,第一时间看后续精彩文章。以为好的话,顺手分享到朋友圈吧,感谢支持。

扫码关注
相关文章
相关标签/搜索