golang爬虫初体验

最近在学习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 实现了该接口。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

切片的内部实现

type slice struct {         array unsafe.Pointer         len   int         ca...

37211
来自专栏LanceToBigData

Jsoup(一)Jsoup详解(官方)

一、Jsoup概述 1.1、简介     jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API...

1K5
来自专栏coder修行路

Python爬虫从入门到放弃(十四)之 Scrapy框架中选择器的用法

Scrapy提取数据有自己的一套机制,被称作选择器(selectors),通过特定的Xpath或者CSS表达式来选择HTML文件的某个部分 Xpath是专门在X...

1998
来自专栏web前端教室

JS基础知识之new操作符 | 前端卧谈会第9期

音频请点此进行收听 音频原文: new操作符是个啥? 就是你搞个 function abc(aa){ this.abc = aa } 然后 var xx = ...

1869
来自专栏前端小叙

js操作DOM在父元素中的结尾添加子节点注意

所以js是不能直接传入字符串的,但是jquery的append可以直接传入html字符串。

1374
来自专栏学海无涯

2.Swift初体验

1513
来自专栏IT开发技术与工作效率

VBA函数与过程简洁教程

1643
来自专栏Young Dreamer

setInterval(code, time)中code传递参数办法

1.使用setInterval的场景 有时我们需要隔一定的时间执行一个方法,这时就会用到setInterval,但是由于这个方法是浏览器模拟出的Timer线程,...

1809
来自专栏大神带我来搬砖

理解HTML工作原理——浏览器如何渲染inline元素中空格

假设有如下的css样式 span { font-size: 300%; border: 1px solid red; } 如下的html代码 <...

2837
来自专栏前端说吧

Sass控制命令及函数知识整理

4206

扫码关注云+社区

领取腾讯云代金券