用Golang寫爬蟲(三) - 使用goquery

在寫爬蟲的時候,想要對HTML內容進行選擇和查找匹配時一般是不直接寫正則表達式的:由於正則表達式可讀性和可維護性比較差。用Python寫爬蟲這方面可選擇的方案很是多了,其中有一個被開發者經常使用的庫pyquery,而Golang也有對應的goquery,能夠說goquery是jQuery的Golang版本實現。借用jQueryCSS選擇器的語法能夠很是方面的實現內容匹配和查找。css

安裝goquery

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

CSS選擇器

這篇文章不會具體介紹選擇器的語法,若是你還不瞭解能夠直接看文末的延伸閱讀連接一。正則表達式

咱們先看看豆瓣電影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">&nbsp;/&nbsp;The Shawshank Redemption</span>
            <span class="other">&nbsp;/&nbsp;月黑高飛(港)  /  刺激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…

代碼地址

完整代碼能夠在這個地址找到。

延伸閱讀

  1. www.w3school.com.cn/cssref/css_…
  2. www.itlipeng.cn/2017/04/25/…
相關文章
相關標籤/搜索