最近在學(xué)習(xí)golang,看網(wǎng)上很多人都喜歡爬豆瓣库物,今天我就寫了一個golang版的爬蟲。對于python爬蟲贷帮,我很了解戚揭,什么dom樹,js異步撵枢,爬蟲技術(shù)棧都是沒問題的民晒。
剛接觸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) {
// 導(dǎo)演
director += s.Text()
//fmt.Println(s.Text())
})
fmt.Println("導(dǎo)演:" + 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)
}
}
}
以上是最簡單版的沃但,可以優(yōu)化的地方還有很多磁滚,比如使用 協(xié)程,請求頭,反爬蟲機(jī)制等宵晚。
主要使用的就是 goquery這個庫垂攘,當(dāng)然也可以使用正則進(jìn)行匹配。我是拒絕的淤刃。 我很喜歡python中的beautifulsoup晒他。goquery類似jquery,可以直接操作dom樹逸贾。goquery使用的不熟練陨仅,代碼寫的有很多重復(fù)津滞,不優(yōu)雅。
goquery
Go 實(shí)現(xiàn)了類似 jQuery 的功能灼伤,包括鏈?zhǔn)讲僮髡Z法触徐、操作和查詢 HTML 文檔。它基于 Go net/html 包和 CSS 選擇器庫 cascadia饺蔑。由于 net/html 解析器返回的是 DOM 節(jié)點(diǎn)锌介,而不是完整的 DOM 樹,因此猾警,jQuery 的狀態(tài)操作函數(shù)沒有實(shí)現(xiàn)(像 height()孔祸,css(),detach())发皿。
由于 net/html 解析器要求文檔必須是 UTF-8 編碼崔慧,因此 goquery 庫也有此要求。如果文檔不是 UTF-8 編碼穴墅,使用者需要自己轉(zhuǎn)換惶室。進(jìn)行編碼轉(zhuǎn)換,可以使用如下庫:
iconv 的 Go 封裝玄货,如:github.com/djimenez/iconv-go
官方提供的 text 子倉庫皇钞,text/encoding,用于其他編碼和 UTF-8 之間進(jìn)行轉(zhuǎn)換
除了實(shí)現(xiàn)和 jQuery 類似的功能外松捉,在函數(shù)名方面夹界,也盡量和 jQuery 保持一致,也支持鏈?zhǔn)秸Z法隘世。
2 goquery 提供的主要類型和方法
2.1 Document
Document 代表一個將要被操作的 HTML 文檔可柿,不過,和 jQuery 不同丙者,它裝載的是 DOM 文檔的一部分复斥。
type Document struct {
*Selection
Url *url.URL
rootNode *html.Node
}
因?yàn)?Document 中內(nèi)嵌了一個 Selection 類型,因此械媒,Document 可以直接使用 Selection 類型的方法目锭。
有五種方法獲取一個 Document 實(shí)例,分別是從一個 URL 創(chuàng)建滥沫、從一個 *html.Node 創(chuàng)建侣集、從一個 io.Reader 創(chuàng)建、從一個 *http.Response 創(chuàng)建和從一個已有的 Document Clone 一個兰绣。
2.2 Selection
Selection 代表符合特定條件的節(jié)點(diǎn)集合世分。
type Selection struct {
Nodes []*html.Node
document *Document
prevSel *Selection
}
一般地,得到了 Document 實(shí)例后缀辩,通過 Dcoument.Find 方法獲取一個 Selection 實(shí)例臭埋,然后像 jQuery 一樣使用鏈?zhǔn)秸Z法和方法操作它踪央。
Selection 類型提供的方法可以分為如下幾大類(注意,3個點(diǎn)(…)表示有重載的方法):
1)類似函數(shù)的位置操作
– Eq()
– First()
– Get()
– Index…()
– Last()
– Slice()
2)擴(kuò)大 Selection 集合(增加選擇的節(jié)點(diǎn))
– Add…()
– AndSelf()
– Union(), which is an alias for AddSelection()
3)過濾方法瓢阴,減少節(jié)點(diǎn)集合
– End()
– Filter…()
– Has…()
– Intersection(), which is an alias of FilterSelection()
– Not…()
4)循環(huán)遍歷選擇的節(jié)點(diǎn)
– Each()
– EachWithBreak()
– Map()
5)修改文檔
– After…()
– Append…()
– Before…()
– Clone()
– Empty()
– Prepend…()
– Remove…()
– ReplaceWith…()
– Unwrap()
– Wrap…()
– WrapAll…()
– WrapInner…()
6)檢測或獲取節(jié)點(diǎn)屬性值
– Attr(), RemoveAttr(), SetAttr()
– AddClass(), HasClass(), RemoveClass(), ToggleClass()
– Html()
– Length()
– Size(), which is an alias for Length()
– Text()
7)查詢或顯示一個節(jié)點(diǎn)的身份
– Contains()
– Is…()
8)在文檔樹之間來回跳轉(zhuǎn)(常用的查找節(jié)點(diǎn)方法)
– 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 節(jié)點(diǎn)和編譯過的選擇器字符串。Cascadia’s Selector 實(shí)現(xiàn)了該接口荣恐。