golang goquery selector选择器使用示例大全
作者:如此风景 发布时间:2023-10-14 15:40:58
引言
最近研究Go爬虫相关的知识,使用到goquery这个库比较多,尤其是对爬取到的HTML进行选择和查找匹配的内容时,goquery的选择器使用尤其多,而且还有很多不常用但又很有用的选择器,这里总结下,以供参考。
如果大家以前做过前端开发,对jquery不会陌生,goquery类似jquery,它是jquery的go版本实现。使用它,可以很方便的对HTML进行处理。
基于HTML Element 元素的选择器
这个比较简单,就是基于a
,p
等这些HTML的基本元素进行选择,这种直接使用Element名称作为选择器即可。
比如dom.Find("div")
。
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选择器的即可。
如果有相同的ID,但是它们又分别属于不同的HTML元素怎么办?有好办法,和Element结合起来。比如我们筛选元素为div
,并且id
是div1
的元素,就可以使用Find(div#div1)
这样的筛选器进行筛选。
所以这类筛选器的语法为Find(element#id)
,这是常用的组合方法,比如后面讲的过滤器也可以采用这种方式组合使用。
Class选择器
class
也是HTML中常用的属性,我们可以通过class
选择器来快速的筛选需要的HTML元素,它的用法和ID
选择器类似,为Find(".class")
。
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())
})
}
以上示例中,就筛选出来class
为name
的这个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
这个属性值为name
的div
元素。
当然我们这里以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
,结果是DIV1、DIV2、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")
,中间是一个加号(+),+号前后也是选择器。
prev~next选择器
有相邻就有兄弟,兄弟选择器就不一定要求相邻了,只要他们共有一个父元素就可以。
dom.Find("div[lang=zh]~p").Each(func(i int, selection *goquery.Selection) {
fmt.Println(selection.Text())
})
刚刚的例子,只需要把+
号换成~
号,就可以把P2
也筛选出来,因为P2
、P1
和DIV1
都是兄弟。
兄弟选择器的语法是("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
后,就只有DIV1
和DIV4
了,因为只有这两个是他们父元素的第一个子元素,其他的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不同的选择器,可以让我们的抓取工作事半功倍,大大提升爬虫的效率。
来源:https://juejin.cn/post/7145373304117788708


猜你喜欢
- 实例如下:# -*- coding: utf-8 -*-"""Spyder EditorThis tempor
- 一.数值类型Mysql支持所有标准SQL中的数值类型,其中包括严格数据类型(INTEGER,SMALLINT,DECIMAL,NUMBERI
- 本文实例讲述了Python实现的计数排序算法。分享给大家供大家参考,具体如下:计数排序是一种非常快捷的稳定性强的排序方法,时间复杂度O(n+
- 1.先用 for 循环取for item in l: if isinstance(item ,list): &nb
- explain用于获取查询执行计划信息,一、语法只需要在select前加上explain即可,如:mysql> explain sel
- 模块导入方式: import osos模块是Python标准库中的一个用于访问操作系统相关功能的模块,os模块提供了一种可移植的使
- 目录一、时间序列数据的生成二、Pandas设置索引三、 时间序列数据的截取四、Pandas重复值处理4.1 查询是否有重复值4.2 去除重复
- 一、window环境A. 导出.sql1. 导出整个数据库mysqldump -u 用户名 -p 数据库名 > 导出的文件名mysql
- sympy有个vector 模块,里面提供了求解标量场、向量场的梯度、散度、旋度等计算,官方参考连接:http://docs.sympy.o
- 1.背景在python运行一些,计算复杂度比较高的函数时,服务器端单核CPU的情况比较耗时,因此需要多CPU使用多进程加快速度2.函数要求笔
- (一)连接连接通常来自Web服务器,下面列出了一些与连接有关的参数,以及该如何设置它们。1、max_connections这是Web服务器允
- write()方法把字符串str写入文件。没有返回值。由于缓冲,字符串可能不实际显示文件,直到flush()或close()方法
- 有时候会碰到行转列的需求(也就是将列的值作为列名称),通常我都是用 CASE END + 聚合函数来实现的。如下:declare @t ta
- 前言要在pandas.DataFrame中的任何位置检索或更改数据,可以使用at,iat,loc,iloc。位置的指定方法at,loc:行标
- 本文实例讲述了Sanic框架异常处理与中间件操作。分享给大家供大家参考,具体如下:简介Sanic是一个类似Flask的Python 3.5+
- 问题描述在我们做项目中,常见的是横向表格,但是偶尔的需求,也会做竖向的表格。比如下图这样的竖向表格:我们看到这样的效果图,第一时间想到的是使
- 引言日常开发中,我们经常会使用到group by。亲爱的小伙伴,你是否知道group by的工作原理呢?group by和having有什么
- 第一章:基本的圆角框第二章:透明圆角化背景图片第三章:圆角化图片 第四章:CSS圆角框组件 V1.0在上面的案例中,我只给出最为原始的圆角框
- 安装淘宝镜像:要安装Angular4。于是我对着一股浓郁口音的视频开启了Angular4安装之旅。那口音说了,ang哥乐4不是那么好装的,由
- 主键自增MySQL 提供了主键自增机制 AUTO_INCREMENT. 对主键使用, 保证了主键的唯一性.注意:自增长必须与主键字段配合使用