Colly框架可以快速发起请求,接收服务器响应。但如果我们需要分析返回的HTML代码,这时候仅仅使用Colly就有点吃力。而goquery库是一个使用Go语言写成的HTML解析库,功能更加强大。
goquery将jQuery的语法和特性引入进来,所以可以更灵活地选择采集内容的数据项,就像jQuery那样的方式来操作DOM文档,使用起来非常的简便。goquery主要的结构:
type Document struct {
*Selection
Url *url.URL
rootNode *html.Node
}
Document 嵌入了Selection 类型,因此,Document 可以直接使用 Selection 类型的方法。我们可以通过下面四种方式来初始化得到*Document对象。
func NewDocumentFromNode(root *html.Node) *Document
func NewDocument(url string) (*Document, error)
func NewDocumentFromReader(r io.Reader) (*Document, error)
func NewDocumentFromResponse(res *http.Response) (*Document, error)
Selection 是重要的一个结构体,解析中最重要,最核心的方法方法都由它提供。
type Selection struct {
Nodes []*html.Node
document *Document
prevSel *Selection
}
下面我们开始了解下怎么使用goquery:
- 首先,要确定已经下载安装这个第三方包:
go get github.com/PuerkitoBio/goquery
- 接下来在代码中导入包:
import "github.com/PuerkitoBio/goquery"
goquery的主要用法是选择器,需要借鉴jQuery的特性,多加练习就能很快掌握。限于篇幅,这里只能简单介绍了goquery的大概情况。
goquery可以直接发送url请求,获得响应后得到HTML代码。但goquery主要擅长于HTML代码分析,而Colly在爬虫抓取管理调度上有优势,所以下面以Colly作为爬虫框架,goquery作为HTML分析器,看看怎么抓取并分析页面内容:
package main
import (
"bytes"
"fmt"
"log"
"net/url"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/gocolly/colly"
)
func main() {
urlstr := "https://news.baidu.com"
u, err := url.Parse(urlstr)
if err != nil {
log.Fatal(err)
}
c := colly.NewCollector()
// 超时设定
c.SetRequestTimeout(100 * time.Second)
// 指定Agent信息
c.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36"
c.OnRequest(func(r *colly.Request) {
// Request头部设定
r.Headers.Set("Host", u.Host)
r.Headers.Set("Connection", "keep-alive")
r.Headers.Set("Accept", "*/*")
r.Headers.Set("Origin", u.Host)
r.Headers.Set("Referer", urlstr)
r.Headers.Set("Accept-Encoding", "gzip, deflate")
r.Headers.Set("Accept-Language", "zh-CN, zh;q=0.9")
})
c.OnHTML("title", func(e *colly.HTMLElement) {
fmt.Println("title:", e.Text)
})
c.OnResponse(func(resp *colly.Response) {
fmt.Println("response received", resp.StatusCode)
// goquery直接读取resp.Body的内容
htmlDoc, err := goquery.NewDocumentFromReader(bytes.NewReader(resp.Body))
// 读取url再传给goquery,访问url读取内容,此处不建议使用
// htmlDoc, err := goquery.NewDocument(resp.Request.URL.String())
if err != nil {
log.Fatal(err)
}
// 找到抓取项 <div class="hotnews" alog-group="focustop-hotnews"> 下所有的a解析
htmlDoc.Find(".hotnews a").Each(func(i int, s *goquery.Selection) {
band, _ := s.Attr("href")
title := s.Text()
fmt.Printf("热点新闻 %d: %s - %s\n", i, title, band)
c.Visit(band)
})
})
c.OnError(func(resp *colly.Response, errHttp error) {
err = errHttp
})
err = c.Visit(urlstr)
}
上面代码中,goquery先通过 goquery.NewDocumentFromReader
生成文档对象htmlDoc。有了htmlDoc就可以使用选择器,而选择器的目的主要是定位:htmlDoc.Find(".hotnews a").Each(func(i int, s *goquery.Selection)
,找到文档中的。
有关选择器Find()方法的使用语法,是不是有些熟悉的感觉,没错就是jQuery的样子。
在goquery中,常用大概有以下选择器:
选择器 | 说明 |
---|---|
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元素,区分大小写 |
parent>child选择器 如果我们想筛选出某个元素下符合条件的子元素,我们就可以使用子元素筛选器,它的语法为Find("parent>child"),表示筛选parent这个父元素下,符合child这个条件的最直接(一级)的子元素。
prev+next相邻选择器 假设我们要筛选的元素没有规律,但是该元素的上一个元素有规律,我们就可以使用这种下一个相邻选择器来进行选择。
prev~next选择器 有相邻就有兄弟,兄弟选择器就不一定要求相邻了,只要他们共有一个父元素就可以。
Colly + goquery 是抓取网络内容的利器,使用上极其方便。如今动态渲染的页面越来越多,爬虫们或多或少都需要用到headless browser来渲染待爬取的页面,这里推荐chromedp,开源网址:https://github.com/chromedp/chromedp
下一节:在Go语言开发的WEB框架中,有两款著名WEB框架的命名都以酒有关:Martini( 马丁尼)和Gin(杜松子酒),由于我不擅于饮酒所以这两种酒的优劣暂不做评价,但说WEB框架相比较的话,Gin要比Martini强很多。