golang爬虫初体验

作者: 若与 | 来源:发表于2018-07-30 00:06 被阅读17次

    最近在学习golang,看网上很多人都喜欢爬豆瓣,今天我就写了一个golang版的爬虫。对于python爬虫,我很了解,什么dom树,js异步,爬虫技术栈都是没问题的。

    刚接触golang爬虫,今天写了一个很简单的爬虫,就是使用2个库,一个http、goquery

    直接上代码

    package main
    
    import (
        "net/http"
        "fmt"
        "github.com/PuerkitoBio/goquery"
        "strconv"
    )
    
    func GetMovie(url string) {
        fmt.Println(url)
        resp, err := http.Get(url)
        if err != nil {
            panic(err)
        }
        //bodyString, err := ioutil.ReadAll(resp.Body)
        //fmt.Println(string(bodyString))
        if resp.StatusCode != 200 {
            fmt.Println("err")
        }
    
        doc, err := goquery.NewDocumentFromReader(resp.Body)
        if err != nil {
            panic(err)
        }
    
        //
    
        doc.Find("#content h1").Each(func(i int, s *goquery.Selection) {
            // name
            fmt.Println("name:" + s.ChildrenFiltered(`[property="v:itemreviewed"]`).Text())
            // year
            fmt.Println("year:" + s.ChildrenFiltered(`.year`).Text())
        })
    
        // #info > span:nth-child(1) > span.attrs
        director := ""
        doc.Find("#info span:nth-child(1) span.attrs").Each(func(i int, s *goquery.Selection) {
            // 导演
            director += s.Text()
            //fmt.Println(s.Text())
        })
        fmt.Println("导演:" + director)
        //fmt.Println("\n")
    
        pl := ""
        doc.Find("#info span:nth-child(3) span.attrs").Each(func(i int, s *goquery.Selection) {
            pl += s.Text()
        })
        fmt.Println("编剧:" + pl)
    
        charactor := ""
        doc.Find("#info span.actor span.attrs").Each(func(i int, s *goquery.Selection) {
            charactor += s.Text()
        })
        fmt.Println("主演:" + charactor)
    
        typeStr := ""
        doc.Find("#info > span:nth-child(8)").Each(func(i int, s *goquery.Selection) {
            typeStr += s.Text()
        })
        fmt.Println("类型:" + typeStr)
    }
    
    func GetToplist(url string) []string {
        var urls []string
        resp, err := http.Get(url)
        if err != nil {
            panic(err)
        }
        //bodyString, err := ioutil.ReadAll(resp.Body)
        //fmt.Println(string(bodyString))
        if resp.StatusCode != 200 {
            fmt.Println("err")
        }
    
        doc, err := goquery.NewDocumentFromReader(resp.Body)
        if err != nil {
            panic(err)
        }
    
        doc.Find("#content div div.article ol li div div.info div.hd a").Each(func(i int, s *goquery.Selection) {
            // year
            fmt.Printf("%v", s)
            herf, _ := s.Attr("href")
            urls = append(urls, herf)
        })
        return urls
    }
    
    func main() {
        url := "https://movie.douban.com/top250?start="
        var urls []string
        var newUrl string
        fmt.Println("%v", urls)
        for i := 0; i < 10; i++ {
            start := i * 25
            newUrl = url + strconv.Itoa(start)
            urls = GetToplist(newUrl)
    
            for _, url := range urls {
                GetMovie(url)
            }
        }
    }
    
    

    以上是最简单版的,可以优化的地方还有很多,比如使用 协程,请求头,反爬虫机制等。


    主要使用的就是 goquery这个库,当然也可以使用正则进行匹配。我是拒绝的。 我很喜欢python中的beautifulsoup。goquery类似jquery,可以直接操作dom树。goquery使用的不熟练,代码写的有很多重复,不优雅。

    goquery

    Go 实现了类似 jQuery 的功能,包括链式操作语法、操作和查询 HTML 文档。它基于 Go net/html 包和 CSS 选择器库 cascadia。由于 net/html 解析器返回的是 DOM 节点,而不是完整的 DOM 树,因此,jQuery 的状态操作函数没有实现(像 height(),css(),detach())。

    由于 net/html 解析器要求文档必须是 UTF-8 编码,因此 goquery 库也有此要求。如果文档不是 UTF-8 编码,使用者需要自己转换。进行编码转换,可以使用如下库:
    iconv 的 Go 封装,如:github.com/djimenez/iconv-go
    官方提供的 text 子仓库,text/encoding,用于其他编码和 UTF-8 之间进行转换

    除了实现和 jQuery 类似的功能外,在函数名方面,也尽量和 jQuery 保持一致,也支持链式语法。

    2 goquery 提供的主要类型和方法

    2.1 Document

    Document 代表一个将要被操作的 HTML 文档,不过,和 jQuery 不同,它装载的是 DOM 文档的一部分。

    type Document struct {
        *Selection
        Url      *url.URL
        rootNode *html.Node
    }
    
    

    因为 Document 中内嵌了一个 Selection 类型,因此,Document 可以直接使用 Selection 类型的方法。

    有五种方法获取一个 Document 实例,分别是从一个 URL 创建、从一个 *html.Node 创建、从一个 io.Reader 创建、从一个 *http.Response 创建和从一个已有的 Document Clone 一个。

    2.2 Selection

    Selection 代表符合特定条件的节点集合。

    type Selection struct {
        Nodes    []*html.Node
        document *Document
        prevSel  *Selection
    }
    

    一般地,得到了 Document 实例后,通过 Dcoument.Find 方法获取一个 Selection 实例,然后像 jQuery 一样使用链式语法和方法操作它。

    Selection 类型提供的方法可以分为如下几大类(注意,3个点(…)表示有重载的方法):

    1)类似函数的位置操作

    – Eq()
    – First()
    – Get()
    – Index…()
    – Last()
    – Slice()

    2)扩大 Selection 集合(增加选择的节点)

    – Add…()
    – AndSelf()
    – Union(), which is an alias for AddSelection()

    3)过滤方法,减少节点集合

    – End()
    – Filter…()
    – Has…()
    – Intersection(), which is an alias of FilterSelection()
    – Not…()

    4)循环遍历选择的节点

    – Each()
    – EachWithBreak()
    – Map()

    5)修改文档

    – After…()
    – Append…()
    – Before…()
    – Clone()
    – Empty()
    – Prepend…()
    – Remove…()
    – ReplaceWith…()
    – Unwrap()
    – Wrap…()
    – WrapAll…()
    – WrapInner…()

    6)检测或获取节点属性值

    – Attr(), RemoveAttr(), SetAttr()
    – AddClass(), HasClass(), RemoveClass(), ToggleClass()
    – Html()
    – Length()
    – Size(), which is an alias for Length()
    – Text()

    7)查询或显示一个节点的身份

    – Contains()
    – Is…()

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

    – Children…()
    – Contents()
    – Find…()
    – Next…()
    – Parent[s]…()
    – Prev…()
    – Siblings…()

    2.3 Matcher 接口

    type Matcher interface {
        Match(*html.Node) bool
        MatchAll(*html.Node) []*html.Node
        Filter([]*html.Node) []*html.Node
    }
    

    该接口定义了一些方法,用于匹配 HTML 节点和编译过的选择器字符串。Cascadia’s Selector 实现了该接口。

    相关文章

      网友评论

        本文标题:golang爬虫初体验

        本文链接:https://www.haomeiwen.com/subject/jmtxvftx.html