在寫爬蟲的時候,想要對HTML內容進行選擇和查找匹配時一般是不直接寫正則表達式的:由於正則表達式可讀性和可維護性比較差。用Python寫爬蟲這方面可選擇的方案很是多了,其中有一個被開發者經常使用的庫pyquery,而Golang也有對應的goquery,能夠說goquery是jQuery的Golang版本實現。借用jQueryCSS選擇器的語法能夠很是方面的實現內容匹配和查找。css
goquery是第三方庫,須要手動安裝:git
❯ go get github.com/PuerkitoBio/goquery
複製代碼
goquery向外暴露的結構主要是goquery.Document,通常是由2種方法建立的:github
doc, error := goquery.NewDocumentFromReader(reader io.Reader)
doc, error := goquery.NewDocument(url string)
複製代碼
第二種直接傳入了url,可是每每咱們會對請求作不少定製(如添加頭信息、設置Cookie等),因此經常使用的是第一種方法,咱們的代碼也要作對應的改動:golang
import (
"fmt"
"log"
"net/http"
"strconv"
"time"
"github.com/PuerkitoBio/goquery"
)
func fetch(url string) *goquery.Document {
...
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
log.Fatal(err)
}
return doc
複製代碼
原來是把res.Body轉成字符返回,如今直接返回goquery.Document類型的doc了web
這篇文章不會具體介紹選擇器的語法,若是你還不瞭解能夠直接看文末的延伸閱讀連接一。正則表達式
咱們先看看豆瓣電影Top250單個條目的部分相關的HTML代碼:bash
<ol class="grid_view">
<li>
<div class="item">
<div class="info">
<div class="hd">
<a href="https://movie.douban.com/subject/1292052/" class="">
<span class="title">肖申克的救贖</span>
<span class="title"> / The Shawshank Redemption</span>
<span class="other"> / 月黑高飛(港) / 刺激1995(臺)</span>
</a>
<span class="playable">[可播放]</span>
</div>
</div>
</div>
</li>
....
</ol>
複製代碼
仍是原來的需求:得到條目ID和標題。此次須要把parseUrls的邏輯改爲使用goquery的版本:函數
func parseUrls(url string, ch chan bool) {
doc := fetch(url)
doc.Find("ol.grid_view li").Find(".hd").Each(func(index int, ele *goquery.Selection) {
movieUrl, _ := ele.Find("a").Attr("href")
fmt.Println(strings.Split(movieUrl, "/")[4], ele.Find(".title").Eq(0).Text())
})
time.Sleep(2 * time.Second)
ch <- true
}
複製代碼
doc.Find的參數就是css選擇器,並且Find支持鏈式調用。這裏的意思就是先找雷鳴是"grid_view"的全部ol下的li元素,而後再找li元素裏面以hd爲名字的元素(看上面的HTML能夠知道是div)。Find找到的結果是列表,須要使用Each方法循環得到,能夠傳遞一個包含索引index和子元素ele參數的函數,得到具體內容的邏輯就在這個函數中。post
在上面的例子中,類名叫作title的span一共有2個,因此須要取第一個(用Eq(0)),Text方法能夠得到元素的內容。而得到條目ID的方法是先拿到條目頁面連接(用Attr得到href屬性,注意,它返回2個參數,第一個是屬性值,第二是是否存在這個屬性)。這樣就拿到了ID和標題啦,是否是可讀性和可維護性高了不少呢?fetch
PS:其實爬蟲練習的目的已經達到了,得到更多內容就是多寫些邏輯罷了。
原文地址: strconv.com/posts/web-c…
完整代碼能夠在這個地址找到。