本文共 11960 字,大约阅读时间需要 39 分钟。
总体就是只要用过jquery,goquery就很简单了。这里整理了网上几篇学习的文章。
goquery为Go语言带来了类似于jQuery的语法和一组特性。它基于Go的net/html
包和CSS Selector库cascadia。由于net/html
解析器返回节点,而不是功能齐全的DOM树,因此jQuery的有状态操作函数(如height()
,css()
,detach()
)已经停止。
此外,因为net/html
解析器需要UTF-8编码,所以goquery也是如此:调用者有责任确保源文档提供UTF-8编码的HTML。
go get github.com/PuerkitoBio/goquery
doc,err := goquery.NewDocumentFromReader(reader io.Reader)doc,err := goquery.NewDocument(url string)doc,err := goquery.NewDocument(strings.NewReader("这里是内容
"))
Eq(index int) *Selection //根据索引获取某个节点集First() *Selection //获取第一个子节点集Get(index int) *html.Node //根据索引获取一个节点Index...() int //返回选择对象中第一个元素的位置Last() *Selection //获取最后一个子节点集Slice(start, end int) *Selection //根据起始位置获取子节点集
Add...()AndSelf()Union() // AddSelection()的别名
End()Filter...()Has...()Intersection() //FilterSelection()的别名Not...()
Each(f func(int, *Selection)) *Selection //遍历EachWithBreak(f func(int, *Selection) bool) *Selection //可中断遍历Map(f func(int, *Selection) string) (result []string) //返回字符串数组
After...()Append...()Before...()Clone()Empty()Prepend...()Remove...()ReplaceWith...()Unwrap()Wrap...()WrapAll...()WrapInner...()
Attr*(), RemoveAttr(), SetAttr() //获取,移除,设置属性的值AttrOr(e string,d string) //获取对应的标签属性。这个可以设置第二个参数。获取的默认值,如果获取不到默认调用对应默认值AddClass(), HasClass(), RemoveClass(), ToggleClass()Html() //获取该节点的htmlLength() //返回该Selection的元素个数Size() //Length()的别名Text() //获取该节点的文本值
Contains() //获取当前节点下的所有节点Is...()
在文档树之间来回跳转(常用的查找节点方法)
Children...() //返回selection中各个节点下的孩子节点Contents() //获取当前节点下的所有节点Find...() //查找获取当前匹配的元素Next...() *Selection //获取下一个兄弟节点集,下一个元素NextAll() *Selection //获取后面所有兄弟节点集Parent[s]...()Prev...() *Selection //前一个兄弟节点集,上一个元素Siblings...()
DocumentSelectionMatcher
NodeNameOuterHtml
基于HTML Element 元素的选择器
就是基于a
,p
等这些HTML的基本元素进行选择。 使用方法: 使用语法为dom.Find(“p”)
,匹配文档中所有的p标签。 ele.Find("h2").Find("a") //链式调用
选择器进阶
Find("div[my]") //筛选含有my属性的div元素Find("div[my=zh]") //筛选my属性为zh的div元素Find("div[my!=zh]") //筛选my属性不等于zh的div元素Find("div[my¦=zh]") //筛选my属性为zh或者zh-开头的div元素Find("div[my*=zh]") //筛选my属性包含zh这个字符串的div元素Find("div[my~=zh]") //筛选my属性包含zh这个单词的div元素,单词以空格分开的Find("div[my$=zh]") //筛选my属性以zh结尾的div元素,区分大小写Find("div[my^=zh]") //筛选my属性以zh开头的div元素,区分大小写
>
符号连接, 使用语法dom.Find("div>p")
, 筛选div
标签下的p
标签a
b
c
我想筛选出b所在的标签
使用方法:dom.Find("p[my=a]+p")
筛选出p标签属性my
的值为a
的相邻p
标签。 b
和c
所在标签 使用语法dom.Find("p[my=a]~p")
,筛选出p
标签属性my
的值为a
的兄弟p
标签。ID选择器是我们使用最频繁的,假如我们有2个p
元素,其实我们只需要其中的一个,那么我们只需要给这个标记一个唯一的id即可,这样我们就可以使用id选择器,精确定位了。
dom.Find("#title")
,匹配文档中所有的id=title
的内容 如果多个标签的ID都是title
,我们可以指定某一个标签,如dom.Find(“p#title”)
ele.Find("#title") //根据id查找
类选择跟ID选择器一样都是使用很频繁的,我们可以通过类选择器快速筛选到需要的内容。
使用方法:id选择器以.开头,紧跟着元素class的值, 使用语法为dom.Find(".content1")
,匹配文档中所有的id=title
的元素。 类选择权器跟ID选择器一样,也可以指定某一个标签dom.Find(“div.content1”)
ele.Find(".title") //根据class查找
一个HTML元素都有自己的属性以及属性值,所以我们也可以通过属性和值筛选元素。
使用方法:我们可以通过元素的属性和属性值来筛选数据, 使用语法为dom.Find("p[class=content1]
,匹配文档中所有的p
标 签的class
属性是content1
的元素。 当然我们这里以class
属性为例,还可以用其他属性,比如href
等很多,自定义属性也是可以的。 ele.Attr("href")ele.AttrOr("href", "")
如果我们想同时筛选出div
,span
等元素怎么办?这时候可以采用多个选择器进行组合使用,并且以逗号(,
)分割,Find("selector1, selector2, selectorN")
表示,只要满足其中一个选择器就可以被筛选出来,也就是选择器的或(|
)运算操作。
func main() { html := `DIV1DIV5` 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()) })}
有时候我们选择出来的结果,并不是我们心目中的最优结果,我们希望对其进行过滤。
dom.Find("p:contains(a)")
,筛选出内容包含a的p标签 Find(":has(selector)")
和contains
差不多,只不过这个是包含的是元素节点。 此外还有Find(":empty")
表示筛选出的元素都不能有子元素(包括文本元素),只筛选那些不包含任何子元素的元素。dom.Find("div:contains(DIV2)").Each(func(i int, selection *goquery.Selection) { fmt.Println(selection.Text()) })
Find("p:first-child")
,筛选出第一个p标签。func main() { html := `DIV1P1
DIV2DIV3DIV5P2
` 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()) })}
:first-child
选择器限制的比较死,必须得是第一个子元素,如果该元素前有其他在前面,就不能用:first-child
了,这时候:first-of-type
就派上用场了,它要求只要是这个类型的第一个就可以。
func main() { html := `DIV1P1
DIV2DIV3P2
DIV5` 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()) })}
: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 := `DIV1P1
DIV2DIV3P2
DIV5` 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()) })}
: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 过滤器 和 :only-of-type 过滤器
筛选出父元素中,只有它自己的一个的元素。Find(":only-child")
过滤器,从字面上看,可以猜测出来,它表示筛选的元素,在其父元素中,只有它自己,它的父元素没有其他子元素,才会被匹配筛选出来。 unc main() { html := `DIV1DIV5` 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就不是,所以不能呗筛选出来。
上面的例子,如果想筛选出DIV1
怎么办?可以使用Find(":only-of-type")
,因为它是它的父元素中,唯一的div
元素,这就是:only-of-type
过滤器所要做的,同类型元素只要只有一个,就可以被筛选出来。大家把上面的例子改成:only-of-type
试试,看看是否有DIV1
。
可获得元素所包含的内容
使用方法:ele.Html()ele.Text()
选择器选中的元素可能是多个,如何针对这些元素逐一进行操作呢?这就需要用到遍历。
使用方法:利用Each方法返回两个参数,索引号及当前元素。 使用语法为ele.Find(".item").Each(func(index int, elA *goquery.Selection)
匹配元素中class
属性是item
的元素并进行遍历 ele.Find(".item").Each(func(index int, elA *goquery.Selection){ // 再获得每个元素elA的href属性的值 href, _ := elA.Attr("href") fmt.Println(href)})
func main() { html := `春晓
春眠不觉晓, 处处闻啼鸟。 夜来风雨声, 花落知多少。
`dom,err:=goquery.NewDocumentFromReader(strings.NewReader(html)) if err!=nil{ log.Fatalln(err) } dom.Find("p").Each(func(i int, selection *goquery.Selection) { fmt.Println(selection.Text()) })}//得到结果://春眠不觉晓,//处处闻啼鸟。//夜来风雨声,//花落知多少。
package mainimport ( "fmt" "log" "github.com/PuerkitoBio/goquery")func ExampleScrape() { doc, err := goquery.NewDocument("http://studygolang.com/topics") if err != nil { log.Fatal(err) } /* dhead := doc.Find("head") dTitle := dhead.Find("title") fmt.Printf("title text:%s\n", dTitle.Text()) html, _ := dTitle.Html() fmt.Printf("title html:%s\n", html) metaArr := dhead.Find("meta") for i := 0; i < metaArr.Length(); i++ { d, _ := metaArr.Eq(i).Attr("name") fmt.Println(d) } */ doc.Find("div.wrapper .container .col-lg-9").Each(func(i int, cs *goquery.Selection) { d, _ := cs.Attr("class") fmt.Println(d) })}func main() { ExampleScrape() return doc, err := goquery.NewDocument("http://studygolang.com/topics") if err != nil { log.Fatal(err) } fmt.Println(doc.Html()) //.Html()得到html内容 pTitle := doc.Find("title").Text() //直接提取title的内容 class := doc.Find("h2").Text() fmt.Printf("class:%v\n", class) fmt.Printf("title:%v\n", pTitle) doc.Find(".topics .topic").Each(func(i int, contentSelection *goquery.Selection) { title := contentSelection.Find(".title a").Text() t := contentSelection.Find(".title a") log.Printf("the length;%d", t.Length()) log.Println("第", i+1, "个帖子的标题:", title) }) /* t := doc.Find(".topics .topic") log.Printf("%+v", t) t = doc.Find(".topics") log.Printf("%+v", t) t = doc.Find(".topic") log.Printf("%+v", t) t = doc.Find("div.topic") log.Printf("div.topic:%+v", t) */ t := doc.Find("div.topic").Find(".title a") log.Printf("div.topic.title a:%+v", t) for i := 0; i < t.Length(); i++ { d, _ := t.Eq(i).Attr("href") title, _ := t.Eq(i).Attr("title") fmt.Println(d) fmt.Println(title) }// 输出:// col-lg-9 col-md-8 col-sm-7
网页地址为:http://www.haokongbu.com/dongzuopian/144016.html
c := colly.NewCollector()c.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299"///On every a element which has href attribute call callbackc.OnHTML("body", func(e *colly.HTMLElement) { dom := e.DOM //电影海报 poster, _ := dom.Find(".poster").Find("img").Attr("src") intro := dom.Find(".intro") //电影名称 movieName := intro.Find("h2").Text() lis := intro.Find("ul").Find("li") //电影豆瓣评分 score := lis.Eq(0).Find("font").Text() //时长 length := lis.Eq(1).Find("span").Text() //导演 doctor := lis.Eq(2).Find("span").Text() //年代 time := lis.Eq(3).Find("span").Text() //地区 country := lis.Eq(4).Find("span").Text() //类型 category := lis.Eq(5).Find("span").Text() //语言 language := lis.Eq(6).Find("span").Text() //编剧 scriptwriter := lis.Eq(7).Find("span").Text() //影片别名 alias := lis.Eq(8).Find("span").Text() //上映时间 releaseDate := lis.Eq(10).Find("span").Text() //主演 actors := lis.Eq(11).Find("span").Text() //关键字 keyword := lis.Eq(12).Find("span").Text() baiduyunaddres := dom.Find(".baiduyunaddres").Eq(0) //下载地址 downloadInfos := make([]DownLoadInfo, 0) //在线播放地址 onlinePlayUrls := make([]OnlinePlayUrl, 0) baiduyunaddres.Find("ul").Find("li").Each(func(i int, selection *goquery.Selection) { way := selection.Find("span").Text() href, _ := selection.Find("a").Attr("href") title := selection.Find("a").Text() if way == "在线" { if href != "" { playUrl := "https://www.haokongbu.com" + href movieUrl := GetPlayUrl(playUrl) onlinePlayUrls = append(onlinePlayUrls, OnlinePlayUrl{ Title: title, MovieUrl: movieUrl}) } //fmt.Println(playUrl) } else { downloadInfos = append(downloadInfos, DownLoadInfo{ Way: way, Title: title, Href: href}) } }) //电影简介 introduction, _ := dom.Find(".movietext").Find(".introduction").Html()})c.Visit("http://www.haokongbu.com/dongzuopian/144016.html")
转载地址:http://rtkpi.baihongyu.com/