Go語言colly框架的快速入門
有些人可能認為爬蟲框架和 http client 庫的功能一樣,用 http client 庫也可以寫爬蟲。當(dāng)然,無論用第三方的 http client 庫還是官方的http
庫,都可以寫爬蟲。但術(shù)業(yè)有專攻,爬蟲框架專門為批量爬取設(shè)計,往往擁有并發(fā)控制、隊列、緩存、HTML 解析等一系列開箱即用的 API,能大幅簡化在爬蟲實現(xiàn)過程中的負擔(dān)
Python 中非常知名的爬蟲框架有Scrapy
,Go 中也有一些 star 數(shù)較高的爬蟲框架。colly
就是其中的佼佼者,它 API 簡潔,性能優(yōu)良,開箱即用。今天就來快速學(xué)習(xí)一下吧!
基本使用
首先引入依賴
go get -u github.com/gocolly/colly/...
之后就可以使用colly
,通過Visit
函數(shù)來告知colly 采集器要訪問的 URL
package main import ( "fmt" "github.com/gocolly/colly/v2" ) func main() { c := colly.NewCollector() c.Visit("http://go-colly.org/") }
這樣就行了么?運行下試試,沒有任何輸出。
$ go run main.go
原因在于代碼要求 colly 采集器訪問http://go-colly.org/
,但沒有設(shè)定訪問 URL 成功或者失敗后要執(zhí)行的動作。colly
提供了一系列的回調(diào)函數(shù),用于 URL 訪問和響應(yīng)過程中各種情況的處理
例如,可以設(shè)定訪問 URL 前、響應(yīng)成功、響應(yīng)失敗時不同邏輯的處理
package main import ( "fmt" "github.com/gocolly/colly/v2" ) func main() { c := colly.NewCollector() c.OnRequest(func(r *colly.Request) { fmt.Println("Visiting", r.URL) }) c.OnResponse(func(r *colly.Response) { fmt.Println("Visited", r.Request.URL) }) c.OnError(func(_ *colly.Response, err error) { fmt.Println("Something went wrong:", err) }) c.Visit("http://go-colly.org/") }
colly
提供的回調(diào)和回調(diào)的順序如下圖,每個回調(diào)可以設(shè)置多次,會依次執(zhí)行
常規(guī)配置
配置分兩部分,一部分是 colly 采集器的配置,一部分是 HTTP 的配置
colly 采集器的配置
新建 colly 采集器時指定配置,例如
c := colly.NewCollector( colly.UserAgent("example.com"), colly.DisallowedDomains("baidu.com", "bing.com"), ) //... c.Visit("http://baidu.com/") c.Visit("http://go-colly.org/")
使用環(huán)境變量可以不用重新編譯代碼??疵执蠹覒?yīng)該也能猜到每個環(huán)境變量都有什么作用
注意環(huán)境變量有固定前綴COLLY_
,官方文檔里并沒有說明(坑?。?/p>
COLLY_ALLOWED_DOMAINS
(逗號分隔)COLLY_CACHE_DIR
(string)COLLY_DETECT_CHARSET
(y/n)COLLY_DISABLE_COOKIES
(y/n)COLLY_DISALLOWED_DOMAINS
(逗號分隔)COLLY_IGNORE_ROBOTSTXT
(y/n)COLLY_MAX_BODY_SIZE
(int)COLLY_MAX_DEPTH
(0 代表不限深度)COLLY_PARSE_HTTP_ERROR_RESPONSE
(y/n)COLLY_USER_AGENT
(string)
$ COLLY_DISALLOWED_DOMAINS=baidu.com,bing.com go run main.go
Visiting http://go-colly.org/
Visited http://go-colly.org/
Finished http://go-colly.org/
通過 colly 采集器的屬性進行配置
c := colly.NewCollector() c.UserAgent = "example.com" //... c.Visit("http://go-colly.org/") c.DisallowedDomains = []string{"baidu.com", "bing.com"} c.Visit("http://baidu.com/")
colly 采集器的配置優(yōu)先級就是上面介紹的順序:新建 < 環(huán)境變量 < 實例屬性
HTTP 的配置
colly 默認使用的是 Go 標(biāo)準(zhǔn)庫中的 http client,我們可以進行替換
c := colly.NewCollector() c.WithTransport(&http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, DualStack: true, }).DialContext, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, }
常見功能
作為一個爬蟲,很少會僅抓取一個鏈接,通常會抓取大量的鏈接,甚至?xí)粩喾治霎?dāng)前頁面中的鏈接,繼續(xù)進行深度的爬取。代碼通常會類似下面
為了避免無限制的爬取,可以限制爬取的域名范圍,和訪問深度;
colly 會記錄已經(jīng)爬取過的鏈接,不會再重復(fù)爬取
func main() { c := colly.NewCollector( colly.AllowedDomains("httpbin.org"), colly.MaxDepth(2), ) c.OnHTML("a[href]", func(e *colly.HTMLElement) { link := e.Attr("href") fmt.Printf("Link found: %q -> %s\n", e.Text, link) c.Visit(link) }) c.OnError(func(_ *colly.Response, err error) { fmt.Println("Something went wrong:", err) }) c.Visit("http://httpbin.org/links/20/3") }
并行抓取
colly 默認是串行逐個鏈接進行爬取,想要提高爬取能力最快速簡單的方式就是開啟并行。
除了需要設(shè)置colly.Async(true)
之外,還需要在最后c.Wait()
等待所有并發(fā)的請求執(zhí)行完成
c.Limit
可以針對某一個域名設(shè)置并發(fā)度和發(fā)起每一個請求的延遲時間
func main() { c := colly.NewCollector( colly.AllowedDomains("httpbin.org"), colly.MaxDepth(2), colly.Async(true), ) c.OnHTML("a[href]", func(e *colly.HTMLElement) { link := e.Attr("href") fmt.Printf("Link found: %q -> %s\n", e.Text, link) c.Visit(link) }) c.Limit(&colly.LimitRule{ DomainGlob: "*httpbin.*", Parallelism: 2, Delay: 5 * time.Second, }) c.Visit("http://httpbin.org/links/20/3") c.Wait() }
持久化的外部存儲
默認情況下已訪問的 URL 和 cookie 等信息都是存儲在內(nèi)存中,服務(wù)重新啟動后將會丟失這部分信息,且無法在多個機器直接共享。
當(dāng)我們構(gòu)建一個分布式爬蟲時,我們需要一個公共的用于維護狀態(tài)的持久化存儲,例如 redis 等。
package main import ( "fmt" "github.com/gocolly/colly/v2" "github.com/gocolly/redisstorage" ) func main() { c := colly.NewCollector( colly.AllowedDomains("httpbin.org", "go-colly.org"), colly.MaxDepth(2), ) storage := &redisstorage.Storage{ Address: "127.0.0.1:6379", Password: "", DB: 0, Prefix: "httpbin_test", } err := c.SetStorage(storage) if err != nil { panic(err) } // 清除之前存儲的信息,可選 if err := storage.Clear(); err != nil { panic(err) } defer storage.Client.Close() c.OnHTML("a[href]", func(e *colly.HTMLElement) { link := e.Attr("href") fmt.Printf("Link found: %q -> %s\n", e.Text, link) c.Visit(link) }) // ... c.Visit("http://httpbin.org/links/20/3") }
隊列
可以使用隊列來控制爬取的速率,默認情況下隊列也是在內(nèi)存中的
import ( "fmt" "github.com/gocolly/colly/v2" "github.com/gocolly/colly/v2/queue" ) func main() { q, _ := queue.New( 2, // 消費的進程數(shù) &queue.InMemoryQueueStorage{MaxSize: 10000}, // 默認的隊列,內(nèi)存隊列 ) c := colly.NewCollector( colly.AllowedDomains("httpbin.org", "go-colly.org"), colly.MaxDepth(2), ) c.OnHTML("a[href]", func(e *colly.HTMLElement) { link := e.Attr("href") fmt.Printf("Link found: %q -> %s\n", e.Text, link) q.AddURL(link) }) q.AddURL("http://go-colly.org/") q.Run(c) }
對于構(gòu)建分布式爬蟲來說,可以借助外部的隊列提高整體的消費能力。
package main import ( "fmt" "github.com/gocolly/colly/v2" "github.com/gocolly/colly/v2/queue" "github.com/gocolly/redisstorage" ) func main() { // 創(chuàng)建redis存儲 storage := &redisstorage.Storage{ Address: "127.0.0.1:6379", Password: "", DB: 0, Prefix: "httpbin_test", } q, err := queue.New( 2, // 消費的進程數(shù) storage, ) if err != nil{ panic(err) } c := colly.NewCollector( colly.AllowedDomains("httpbin.org", "go-colly.org"), colly.MaxDepth(2), ) err = c.SetStorage(storage) if err != nil { panic(err) } c.OnHTML("a[href]", func(e *colly.HTMLElement) { link := e.Attr("href") fmt.Printf("Link found: %q -> %s\n", e.Text, link) q.AddURL(link) }) q.AddURL("http://go-colly.org/") q.Run(c) }
總結(jié)
這篇文章作為一個入門介紹,看完后你應(yīng)該能 Get 到普通的 http client 庫和爬蟲庫的區(qū)別了吧。
colly 作為一個爬蟲框架集成了一系列針對爬蟲的 API,想要體驗 colly 的更多能力,建議還是好好閱讀下 colly 的文檔
到此這篇關(guān)于Go語言colly框架的快速入門的文章就介紹到這了,更多相關(guān)Go colly框架內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang利用redis和gin實現(xiàn)保存登錄狀態(tài)校驗登錄功能
這篇文章主要介紹了golang利用redis和gin實現(xiàn)保存登錄狀態(tài)校驗登錄功能,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-01-01Go?interface{}?轉(zhuǎn)切片類型的實現(xiàn)方法
本文主要介紹了Go?interface{}?轉(zhuǎn)切片類型的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02Go?數(shù)據(jù)結(jié)構(gòu)之二叉樹詳情
這篇文章主要介紹了?Go?數(shù)據(jù)結(jié)構(gòu)之二叉樹詳情,二叉樹是一種數(shù)據(jù)結(jié)構(gòu),在每個節(jié)點下面最多存在兩個其他節(jié)點。即一個節(jié)點要么連接至一個、兩個節(jié)點或不連接其他節(jié)點,下文基于GO語言展開二叉樹結(jié)構(gòu)詳情,需要的朋友可以參考一下2022-05-05Golang常用環(huán)境變量說明與設(shè)置詳解
這篇文章主要介紹了Golang常用環(huán)境變量說明與設(shè)置,需要的朋友可以參考下2020-02-02Golang報“import cycle not allowed”錯誤的2種解決方法
這篇文章主要給大家介紹了關(guān)于Golang報"import cycle not allowed"錯誤的2種解決方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以們下面隨著小編來一起看看吧2018-08-08