• goquery的安装以及使用


    goquery的安装以及使用

    安装

    goquery安装失败是缺少net依赖包。
    获取 golang.org/x/net 包,其实只需要以下步骤:

    mkdir -p $GOPATH/src/golang.org/x 
    cd $GOPATH/src/golang.org/x 
    git clone https://github.com/golang/net.git
    

    最后执行

    go get github.com/PuerkitoBio/goquery
    

    导入

    import  "github.com/PuerkitoBio/goquery"
    

    加载页面

    // 请求html页面
      res, err := http.Get("http://metalsucks.net")
      if err != nil {
        // 错误处理
        log.Fatal(err)
      }
      defer res.Body.Close()
      if res.StatusCode != 200 {
        log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
      }
    

    获得document对象

    有多种获得document对象的方法

      // 加载 HTML document对象
      doc, err := goquery.NewDocumentFromReader(res.Body)
      if err != nil {
        log.Fatal(err)
      }
    
    doc,err := goquery.NewDocumentFromReader(reader io.Reader)
    doc,err := goquery.NewDocument(url string)
    doc,err := goquery.NewDocument(strings.NewReader("<p>这里是内容</p>"))
    
    

    选择元素

    1.元素选择器

    基于HTML Element 元素的选择器
    就是基于a,p等这些HTML的基本元素进行选择。
    使用方法
    使用语法为dom.Find(“p”),匹配文档中所有的p标签。

    ele.Find("h2").Find("a") //链式调用
    

    选择器进阶

    • 属性选择器
      一个HTML元素都有自己的属性以及属性值,所以我们也可以通过属性和值筛选元素。
      使用方法 :我们可以通过元素的属性和属性值来筛选数据,使用语法为dom.Find("p[class=content1],匹配文档中所有的 p标签的class属性是content1的元素。
      当然我们这里以class属性为例,还可以用其他属性,比如href等很多,自定义属性也是可以的。
      刚刚我们使用的是完全相等的匹配方式,属性选择器还要很多匹配方式。
    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元素,区分大小写
    
    • parent > child选择器
      如果要筛选出某个元素下的子元素,则使用>符号连接,
      使用语法dom.Find("div>p"), 筛选div标签下的p标签
    • element + next 相邻选择器
      如果要筛选的元素没有规律,但是该元素的上一个元素有规律,我们就可以使用这种下一个相邻选择器来进行选择。
      如:
    <div>
    <p my="a">a</p>
    <p>b</p>
    <p>c</p>
    <div>
    

    我想筛选出b所在的标签
    使用方法:dom.Find("p[my=a]+p")筛选出p标签属性my的值为a的相邻p标签。

    • element~next 兄弟选择器
      如果我们需要筛选同一父元素下,不相邻的标签,可以使用兄弟选择器
      比如想筛选出bc所在标签
      使用语法dom.Find("p[my=a]~p"),筛选出p标签属性my的值为a的兄弟p标签。

    2.ID 选择器

    ID选择器是我们使用最频繁的,假如我们有2个p元素,其实我们只需要其中的一个,那么我们只需要给这个标记一个唯一的id即可,这样我们就可以使用id选择器,精确定位了。
    使用方法:id选择器以#开头,紧跟着元素id的值,
    使用语法为dom.Find("#title") ,匹配文档中所有的id=title的内容
    如果多个标签的ID都是title,我们可以指定某一个标签,如dom.Find(“p#title”)

    ele.Find("#title") //根据id查找
    

    3.Class选择器

    类选择跟ID选择器一样都是使用很频繁的,我们可以通过类选择器快速筛选到需要的内容。
    使用方法:id选择器以.开头,紧跟着元素class的值,
    使用语法为dom.Find(".content1"),匹配文档中所有的id=title的元素。
    类选择权器跟ID选择器一样,也可以指定某一个标签dom.Find(“div.content1”)

    ele.Find(".title") //根据class查找
    

    4.属性选择器

    一个HTML元素都有自己的属性以及属性值,所以我们也可以通过属性和值筛选元素。
    使用方法:我们可以通过元素的属性和属性值来筛选数据,
    使用语法为dom.Find("p[class=content1],匹配文档中所有的p标 签的class属性是content1的元素。
    当然我们这里以class属性为例,还可以用其他属性,比如href等很多,自定义属性也是可以的。

    ele.Attr("href")
    ele.AttrOr("href", "")
    

    5.选择器或(|)运算

    如果我们想同时筛选出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())
    	})
    }
    

    6.过滤器

    有时候我们选择出来的结果,并不是我们心目中的最优结果,我们希望对其进行过滤。

    • :contains过滤器
      筛选出的元素要包含指定的文本,比如我想筛选出包含a的p标签
      使用方法: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())
    	})
    
    • :first-child过滤器 和:first-of-type过滤器
      筛选出的元素要是他们的父元素的第一个子元素,如果不是,则不会被筛选出来。
      使用方法:语法为Find("p:first-child"),筛选出第一个p标签。
    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())
    	})
    }
    

    :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())
    	})
    }
    
    • :last-child 和:last-of-type过滤器
      这两个正好和上面的:first-child:first-of-type相反
    • :nth-child(n) 过滤器
      筛选出的元素是其父元素的第n个元素,n1开始。所以我们可以知道: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())
    	})
    }
    
    • :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 := `<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就不是,所以不能呗筛选出来。

    上面的例子,如果想筛选出DIV1怎么办?可以使用Find(":only-of-type"),因为它是它的父元素中,唯一的div元素,这就是:only-of-type过滤器所要做的,同类型元素只要只有一个,就可以被筛选出来。大家把上面的例子改成:only-of-type试试,看看是否有DIV1

    7.获取内容

    可获得元素所包含的内容
    使用方法

    ele.Html()
    ele.Text()
    

    8.遍历

    选择器选中的元素可能是多个,如何针对这些元素逐一进行操作呢?这就需要用到遍历。
    使用方法:利用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)
    })
    

    内置函数

    1) array.go : 类似数组的定位函数
    Eq(index int) *Selection //根据索引获取某个节点集
    First() *Selection //获取第一个子节点集
    Get(index int) *html.Node //根据索引获取一个节点
    Index...() int //返回选择对象中第一个元素的位置
    Last() *Selection //获取最后一个子节点集
    Slice(start, end int) *Selection //根据起始位置获取子节点集
    
    2)expand.go : 扩展函数
    Add...()
    AndSelf()
    Union() // AddSelection()的别名
    
    3)filter.go : 过滤函数, 用于减少选择范围
    End()
    Filter...()
    Has...()
    Intersection() //FilterSelection()的别名
    Not...()
    
    4)iteration.go : 循环遍历选择节点的函数
    Each(f func(int, *Selection)) *Selection //遍历
    EachWithBreak(f func(int, *Selection) bool) *Selection //可中断遍历
    Map(f func(int, *Selection) string) (result []string) //返回字符串数组
    
    5)manipulation.go : 修改文档的函数
    After...()
    Append...()
    Before...()
    Clone()
    Empty()
    Prepend...()
    Remove...()
    ReplaceWith...()
    Unwrap()
    Wrap...()
    WrapAll...()
    WrapInner...()
    
    6)property.go :检测或获取节点属性值的函数
    Attr*(), RemoveAttr(), SetAttr() //获取,移除,设置属性的值
    AttrOr(e string,d string) //获取对应的标签属性。这个可以设置第二个参数。获取的默认值,如果获取不到默认调用对应默认值
    AddClass(), HasClass(), RemoveClass(), ToggleClass()
    Html() //获取该节点的html
    Length() //返回该Selection的元素个数
    Size() //Length()的别名
    Text() //获取该节点的文本值
    
    7)query.go : 节点查找的函数
    Contains() //获取当前节点下的所有节点
    Is...()
    
    8)traversal.go : 遍历HTML文档树的函数

    在文档树之间来回跳转(常用的查找节点方法)

    Children...() //返回selection中各个节点下的孩子节点
    Contents() //获取当前节点下的所有节点
    Find...() //查找获取当前匹配的元素
    Next...() *Selection //获取下一个兄弟节点集,下一个元素
    NextAll() *Selection //获取后面所有兄弟节点集
    Parent[s]...()
    Prev...() *Selection //前一个兄弟节点集,上一个元素
    Siblings...()
    
    9)type.go : goQuery定义的类型
    Document
    Selection
    Matcher
    
    10)utilities.go : 辅助函数的定义(而不是* Selection上的方法),jQuery没有该部分,但对goquery很有用
    NodeName
    OuterHtml
    

    例子

    入门例子

    func main() {
        html := `<html>
                <body>
                    <h1 id="title">春晓</h1>
                    <p class="content1">
                    春眠不觉晓,
                    处处闻啼鸟。
                    夜来风雨声,
                    花落知多少。
                    </p>
                </body>
                </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 main
    
    import (
        "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
    ", dTitle.Text())
            html, _ := dTitle.Html()
            fmt.Printf("title html:%s
    ", 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
    ", class)
        fmt.Printf("title:%v
    ", 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 callback
    c.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")
    
    

    官方api

    使用参考博客

    安装参考博客

    参考博客

  • 相关阅读:
    查看JAVA占用CPU高的线程日志
    行为面试法(STAR)
    下载mysql document
    win10 子系统ubuntu中文乱码
    java 排序
    进制
    开始转型学习java
    java Collections工具类
    java Map实例
    java Map
  • 原文地址:https://www.cnblogs.com/tomyyyyy/p/14106068.html
Copyright © 2020-2023  润新知