寫爬蟲還在用 python?快來試試 go 語言的爬蟲框架吧

今天爲大家介紹的是一款 go 語言爬蟲框架 -- colly

開始

首先,你可以使用一下命令安裝 colly

go get -u github.com/gocolly/colly/...

其次,構建 Collector,添加事件,然後訪問:

package main

import (
    "fmt"

    "github.com/gocolly/colly"
)

func main() {
    // 初始化 colly
    c := colly.NewCollector(
        // 只採集規定的域名下的內容
        colly.AllowedDomains("hackerspaces.org", "wiki.hackerspaces.org"),
    )

    // 任何具有 href 屬性的標籤都會觸發回調函數
    // 第一個參數其實就是 jquery 風格的選擇器
    c.OnHTML("a[href]", func(e *colly.HTMLElement) {
        link := e.Attr("href")
        fmt.Printf("Link found: %q -> %s\n", e.Text, link)
        // 訪問該網站
        c.Visit(e.Request.AbsoluteURL(link))
    })

    // 在請求發起之前輸出 url
    c.OnRequest(func(r *colly.Request) {
        fmt.Println("Visiting", r.URL.String())
    })

    // 從以下地址開始抓起
    c.Visit("https://hackerspaces.org/")
}

運行以上代碼,會從最開始的地址抓起,一直把規定的兩個域名下的頁面遞歸採集完。看,是不是很簡單很方便!

登錄鑑權

某些網站的某些頁面可能需要登錄狀態才能訪問。Colly 提供 Post 方法用於登錄請求(colly 本身會維護 cookie)。

// authenticate
err := c.Post("http://example.com/login", map[string]string{"username": "admin", "password": "admin"})
if err != nil {
    log.Fatal(err)
}

很多網站可能會有驗證碼、csrf_token 之類的仿網絡攻擊策略。對於 csrf_token,一般都會在頁面的某個位置,比如表單,或者 mate 標籤裏,這些都是很容易獲取到的。對於驗證碼,可以嘗試在控制檯輸入結果或者採用圖片識別的方式。

速率控制

很多內容網站會有防採集策略,所以過快的請求速率很可能導致被封 ip。這裏可以使用 LimitRule 限制採集速度。

// 對於任何域名,同時只有兩個併發請求在請求該域名
c.Limit(&colly.LimitRule{DomainGlob: "*", Parallelism: 2})

上面是一個簡單的例子。除了可以限制域名併發量外,還可以限制間隔時間等。我們看一下 LimitRule 的結構:

type LimitRule struct {
   // 匹配域名的正則表達式
   DomainRegexp string
   // glob 匹配模式
   DomainGlob string
   // 在發起一個新請求時的等待時間
   Delay time.Duration
   // 在發起一個新請求時的隨機等待時間
   RandomDelay time.Duration
   // 匹配到的域名的併發請求數
   Parallelism    int
   waitChan       chan bool
   compiledRegexp *regexp.Regexp
   compiledGlob   glob.Glob
}

隊列與redis存儲支持

某些情況下,我們的爬蟲可能會主動或被動地掛掉,所以一個合理的進度保存有助於我們排除已經爬過的內容。這時候我們就需要用到隊列以及存儲支持。

Colly 本身有文件存儲模式,默認是 未開啓狀態。推薦使用 redis 進行存儲。

urls := []string{
   "http://httpbin.org/",
   "http://httpbin.org/ip",
   "http://httpbin.org/cookies/set?a=b&c=d",
   "http://httpbin.org/cookies",
}

c := colly.NewCollector()

// 創建 redis storage
storage := &redisstorage.Storage{
   Address:  "127.0.0.1:6379",
   Password: "",
   DB:       0,
   Prefix:   "httpbin_test",
}

// 把 storage 設置到 collector 上
err := c.SetStorage(storage)
if err != nil {
   panic(err)
}

// 刪除之前的數據(如果需要)
if err := storage.Clear(); err != nil {
   log.Fatal(err)
}

// 結束後關閉 redis 連接
defer storage.Client.Close()

// 使用 redis 作爲存儲後端,創建請求隊列
// 消費者數量設定爲 2
q, _ := queue.New(2, storage)

c.OnResponse(func(r *colly.Response) {
   log.Println("Cookies:", c.Cookies(r.Request.URL.String()))
})

// 把 url 加入到隊列
for _, u := range urls {
   q.AddURL(u)
}
// 開始採集
q.Run(c)

使用隊列時,在解析到頁面的鏈接後,可以繼續把鏈接的 url 添加到隊列中。

內容解析

內容抓取到了,如何解析並獲取我們想要的內容呢?
html 爲例(colly 也有 xml 等內容解析):

// refentry 內容
c.OnHTML(".refentry", func(element *colly.HTMLElement) {
   // ...
})

OnHtml 第一個參數是 jquery風格的選擇器,第二個參數是 callback,callback 會傳入 HTMLElement 對象。HTMLElement 結構體:
type HTMLElement struct {
   // 標籤的名稱
   Name       string
   Text       string
   attributes []html.Attribute
   // 當前的 request
   Request *Request
   // 當前的 response
   Response *Response
   // 當前節點的 DOM 元素
   DOM *goquery.Selection
   // 在該 callback 回調中,此 element 的索引
   Index int
}

其中,可以通過 DOM 字段操作(增刪節點)、遍歷、獲取節點內容。
DOM 字段是 Selection 類型,該類型提供了大量的方法。如果你用過 jQuery,你一定會覺得熟悉。

舉個栗子,我們想要刪除 h1.refname 標籤,並返回父元素的 html 內容:

c.OnHTML(".refentry", func(element *colly.HTMLElement) {
   titleDom := element.DOM.Find("h1.refname")
   title := titleDom.Text()
   titleDom.Remove()
   
   content, _ := element.DOM.Html()
   // ...
})

其他

除此之外,Colly 還具有其他強大的功能,比如最大遞歸深度、url 過濾、url revisit(默認一個 url 只訪問一次)以及編碼檢測等。這些都可以在官網文檔或者 colly 代碼裏找到影子。

另附上 colly 文檔地址:http://go-colly.org/docs/intr...


bVbjLVY?w=800&h=300

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章