之前一直以為"爬蟲"是一門高大上的技術(shù),但自從遇見goquery之后寥粹,發(fā)現(xiàn)爬取網(wǎng)站也可以這么簡(jiǎn)單变过。
goquery是一個(gè)使用go語言寫的HTML解析庫,它最大的特點(diǎn)就是可以像使用jQuery那樣涝涤,來方便地操作DOM文檔媚狰,相信做過web開發(fā)的人員很快就能掌握其使用方法。
selector(選擇器)
我認(rèn)為selector是這個(gè)框架的靈魂所在阔拳,就是因?yàn)閷?shí)現(xiàn)了類似于jQuery的DOM選擇功能崭孤,才使得框架非常容易使用。
以下是幾個(gè)常用的選擇器糊肠,看著是不是很熟悉:
s.Find("div") // 元素選擇
s.Find("#Content") // id選擇
s.Find(".content") // class選擇
s.Find("div[id=Content]") // 屬性選擇
s.Find("div>p") // 子元素選擇
s.Find("div+p") // 相鄰元素選擇
s.Find("div~p") // 兄弟元素選擇
s.Find("#Content").Text() // 獲取對(duì)象的文本內(nèi)容
s.Find("#Content").Html() // 獲取對(duì)象的html
s.Find("#Content").Attr("src") // 獲取對(duì)象的src屬性值
這里推薦一篇文章辨宠,非常詳細(xì)地介紹了goquery選擇器的各種用法。
實(shí)戰(zhàn)
介紹方面網(wǎng)上有寫的很好的文章货裹,我也沒有什么新的內(nèi)容補(bǔ)充嗤形,所以直接進(jìn)入實(shí)戰(zhàn)部分了。
頁面分析
這里我用goquery爬了豆瓣電影(心疼豆瓣弧圆,好多人把豆瓣電影當(dāng)爬蟲練手)赋兵,通過對(duì)豆瓣電影主頁進(jìn)行分析,發(fā)現(xiàn)電影列表是通過ajax獲取的搔预,然而goquery針對(duì)的只是靜態(tài)的DOM文檔毡惜,對(duì)于動(dòng)態(tài)的數(shù)據(jù)它就無能為力了。
通過觀察斯撮,找到獲取電影列表的url,發(fā)現(xiàn)是get方法獲取的扶叉,那么我們就可以編程構(gòu)造get請(qǐng)求獲取電影列表進(jìn)行處理了勿锅,其有type帕膜、tag、sort溢十、page_limit垮刹、page_start這幾個(gè)參數(shù),操作一下頁面很容易獲取這幾個(gè)參數(shù)值张弛。
使用goquery爬取的是具體的電影詳情頁面荒典,也沒有搞得多復(fù)雜,只獲取一些基本信息用于展示即可吞鸭。
爬取電影詳情頁信息
其實(shí)文字上也沒什么好描述的寺董,看代碼來的更直觀明了,先講一下步驟刻剥,首先自然是要get請(qǐng)求獲取頁面內(nèi)容了遮咖,然后創(chuàng)建一個(gè)goquery解析器,最后使用選擇器獲取需要的數(shù)據(jù)即可造虏。
func GetMovieInfo(url string) *MovieParam {
// get請(qǐng)求獲取頁面
res, err := http.Get(url)
if err != nil {
log.Println(err)
return nil
}
defer res.Body.Close()
if res.StatusCode != 200 {
log.Printf("status code error: %d %s", res.StatusCode, res.Status)
return nil
}
// 創(chuàng)建解析器
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
log.Println(err)
return nil
}
param := MovieParam{}
doc.Find("#content").Each(func(i int, s *goquery.Selection) {
param.Year = s.Find("h1 .year").Text() // 年份
param.Img, _ = s.Find("#mainpic img").Attr("src") // 圖片
param.Summary, _ = s.Find("#link-report span[property]").Html() // 摘要
param.Rating_people = comhelper.StringToInt(s.Find(".rating_people span[property]").Text()) // 評(píng)論人數(shù)
star, _ := s.Find(".bigstar").Attr("class") // 星級(jí)值
param.Bigstar = comhelper.StringToInt(star[len(star)-2 : len(star)])
stars_five := s.Find(".stars5+div+span").Text() // 5星的比例值
param.Stars_five = comhelper.StringToFloat(stars_five[0:len(stars_five)-1], 64)
stars_four := s.Find(".stars4+div+span").Text() // 4星的比例值
param.Stars_four = comhelper.StringToFloat(stars_four[0:len(stars_four)-1], 64)
stars_three := s.Find(".stars3+div+span").Text() // 3星的比例值
param.Stars_three = comhelper.StringToFloat(stars_three[0:len(stars_three)-1], 64)
stars_two := s.Find(".stars2+div+span").Text() // 2星的比例值
param.Stars_two = comhelper.StringToFloat(stars_two[0:len(stars_two)-1], 64)
stars_one := s.Find(".stars1+div+span").Text() // 1星的比例值
param.Stars_one = comhelper.StringToFloat(stars_one[0:len(stars_one)-1], 64)
// 圖片轉(zhuǎn)換成base64
img_url, _ := _download_img(param.Img)
new_img, err := comhelper.ImgToBase64(img_url)
if err == nil && new_img != "" {
param.Img = new_img
}
s.Find("#info").Each(func(ii int, ss *goquery.Selection) {
info, _ := ss.Html()
param.Director = ss.Find("a[rel*=directedBy]").Text() // 導(dǎo)演
film_length, _ := ss.Find("span[property*=runtime]").Attr("content") // 時(shí)長(zhǎng)
param.Film_length = comhelper.StringToInt(film_length)
param.Release_date = ss.Find("span[property*=initialReleaseDate]").Text() // 上映日期
// 獲取類型
tags := ""
ss.Find("span[property*=genre]").Each(func(i int, s *goquery.Selection) {
if tags == "" {
tags += s.Text()
} else {
tags += "/" + s.Text()
}
})
param.Tags = tags
// 獲取主演
actor := ""
ss.Find("a[rel*=starring]").Each(func(i int, s *goquery.Selection) {
if actor == "" {
actor += s.Text()
} else {
actor += "/" + s.Text()
}
})
param.Actor = actor
c_start := strings.Index(info, "<span class=\"pl\">制片國(guó)家/地區(qū):</span>")
c_end := strings.Index(info, "<span class=\"pl\">語言")
param.Country = comhelper.TrimHtml(info[c_start+44 : c_end])
})
})
return ¶m
}
那些有id御吞、class或者特殊屬性的字段最容易獲取了,比較麻煩的是那些沒有明顯特征的字段漓藕,只能通過字符串截取的方法獲取了陶珠,不過也都是些常規(guī)操作,整個(gè)流程下來沒什么難點(diǎn)享钞,這也說明了goquery的簡(jiǎn)單易用揍诽。
成果展示
成果展示以及源碼點(diǎn)擊這里(抱歉,服務(wù)器太貴了嫩与,已脫坑)
遇到的問題
頻繁訪問會(huì)導(dǎo)致ip被鎖住寝姿,不過我也只是練習(xí),所以只是爬取了一點(diǎn)數(shù)據(jù)用來展示划滋。
圖片會(huì)有訪問權(quán)限的問題饵筑,所以我轉(zhuǎn)換成了base64格式存到數(shù)據(jù)庫里,不過在頁面渲染的時(shí)候由于數(shù)據(jù)量過大導(dǎo)致頁面加載巨慢处坪。