我是在windows系統上安裝的go,使用goland編輯。
Hello world:
package main import "fmt" func main() { fmt.Println("Hello, world") }
ctrl+alt+f10運行
下載網頁
這里先從Golang原生http庫開始,直接使用 net/http
包內的函數請求
import "net/http" ... resp, err := http.Get("http://wwww.baidu.com")
所以代碼可以這樣寫
package main import ( "fmt" "io/ioutil" "net/http" ) func main() { fmt.Println("Hello, world") resp, err := http.Get("http://www.baidu.com/") if err != nil { fmt.Println("http get error", err) return } body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("read error", err) return } fmt.Println(string(body)) }
Golang的錯誤處理就是這樣的,習慣就好。
這里更好的做法是把下載方法封裝為函數。
package main import ( "fmt" "io/ioutil" "net/http" ) func main() { fmt.Println("Hello, world") url := "http://www.baidu.com/" download(url) } func download(urlstring) { client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) // 自定義Header req.Header.Set("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)") resp, err := client.Do(req) if err != nil { fmt.Println("http get error", err) return } //函數結束后關閉相關鏈接 defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("read error", err) return } fmt.Println(string(body)) }
解析網頁
go常見的解析器xpath、 jquery 、正則都有,直接搜索即可,我這里偷懶,直接用別人寫好的輪子 collectlinks
,可以提取網頁中所有的鏈接,下載方法 go get -u github.com/jackdanger/collectlinks
package main import ( "fmt" "github.com/jackdanger/collectlinks" "net/http" ) func main() { fmt.Println("Hello, world") url := "http://www.baidu.com/" download(url) } func download(urlstring) { client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) // 自定義Header req.Header.Set("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)") resp, err := client.Do(req) if err != nil { fmt.Println("http get error", err) return } //函數結束后關閉相關鏈接 defer resp.Body.Close() links := collectlinks.All(resp.Body) for _, link := range links { fmt.Println("parse url", link) } }
並發
Golang使用關鍵字 go
即可開啟一個新的 go 程,也叫 goroutine
,使用 go 語句開啟一個新的 goroutine 之后,go 語句之后的函數調用將在新的 goroutine 中執行,而不會阻塞當前的程序執行。所以使用Golang可以很容易寫成異步IO。
package main import ( "fmt" "github.com/jackdanger/collectlinks" "net/http" ) func main() { fmt.Println("Hello, world") url := "http://www.baidu.com/" queue := make(chan string) go func() { queue <- url }() for uri := range queue { download(uri, queue) } } func download(urlstring, queuechan string) { client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) // 自定義Header req.Header.Set("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)") resp, err := client.Do(req) if err != nil { fmt.Println("http get error", err) return } //函數結束后關閉相關鏈接 defer resp.Body.Close() links := collectlinks.All(resp.Body) for _, link := range links { fmt.Println("parse url", link) go func() { queue <- link }() } }
現在的流程是main有一個for循環讀取來自名為queue的通道,download下載網頁和鏈接解析,將發現的鏈接放入main使用的同一隊列中,並再開啟一個新的goroutine去抓取形成無限循環。
這里對於新手來說真的不好理解,涉及到Golang的兩個比較重要的東西:goroutine和channels,這個我也不大懂,這里也不多講了,以后有機會細說。
官方:A goroutine is a lightweight thread managed by the Go runtime.翻譯過來就是:Goroutine是由Go運行時管理的輕量級線程。channels是連接並發goroutine的管道,可以理解為goroutine通信的管道。 可以將值從一個goroutine發送到通道,並將這些值接收到另一個goroutine中。對這部分有興趣的可以去看文檔。
好了,到這里爬蟲基本上已經完成了,但是還有兩個問題:去重、鏈接是否有效。
鏈接轉為絕對路徑
package main import ( "fmt" "github.com/jackdanger/collectlinks" "net/http" "net/url" ) func main() { fmt.Println("Hello, world") url := "http://www.baidu.com/" queue := make(chan string) go func() { queue <- url }() for uri := range queue { download(uri, queue) } } func download(urlstring, queuechan string) { client := &http.Client{} req, _ := http.NewRequest("GET", url, nil) // 自定義Header req.Header.Set("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)") resp, err := client.Do(req) if err != nil { fmt.Println("http get error", err) return } //函數結束后關閉相關鏈接 defer resp.Body.Close() links := collectlinks.All(resp.Body) for _, link := range links { absolute := urlJoin(link, url) if url != " " { fmt.Println("parse url", absolute) go func() { queue <- absolute }() } } } func urlJoin(href, basestring)string { uri, err := url.Parse(href) if err != nil { return " " } baseUrl, err := url.Parse(base) if err != nil