1. 程式人生 > >【Go 入門學習】第一篇關於 Go 的部落格--Go 爬蟲初體驗

【Go 入門學習】第一篇關於 Go 的部落格--Go 爬蟲初體驗

一、寫在前面

  其實早就該寫這一篇部落格了,為什麼一直沒有寫呢?還不是因為忙不過來(實際上只是因為太懶了)。不過好了,現在終於要開始寫這一篇部落格了。在看這篇部落格之前,可能需要你對 Go 這門語言有些基本的瞭解,比如基礎語法之類的。話不多說,進入正題。

 

二、Go 環境配置

1.安裝配置

  在學習一門語言時,第一步就是環境配置了,Go 也不例外,下面就是 Windows 下 Go 開發環境的配置過程了。

  首先你需要下載 Go 的安裝包,可以開啟 Go 語言中文網下載,地址為:https://studygolang.com/dl。

  下載完成後開啟安裝(例如安裝到 E:\Go 目錄),然後配置環境變數,將安裝目錄下的 bin 目錄路徑加入環境變數中。這一步完成後開啟命令列,輸入 go version,若出現版本資訊則表明配置成功。

2.配置 GOPATH 和 GOROOT

  除了要將 bin 目錄加入到環境變數中,還要配置 GOPATH 和 GOROOT,步驟如下:

  在使用者變數中新建變數 GOPATH:

  

   在系統變數中新建變數 GOROOT:

  

 3. 選擇 IDE

  在 IDE 的選擇上,我比較推薦使用 jetbrains 家的 GoLand,功能強大,使用起來也很方便。

 

三、下載網頁

  下載網頁使用的是 Go 中原生的 http 庫,在使用前需要導包,和 Python 一樣用 import 匯入即可。如果要傳送 GET 請求,可以直接使用 http 中的 Get() 方法,例如:

 1 package main
 2 
 3 import (
 4     "fmt"
 5     "net/http"
 6 )
 7 
 8 func main () {
 9     html, err := http.Get("https://www.baidu.com/")
10     if err != nil {
11         fmt.Println(err)
12     }
13     fmt.Println(html)
14 }

  Get() 方法有兩個返回值,html 表示請求的結果,err 表示錯誤,這裡必須對 err 做判斷,Go 語言的錯誤機制就是這樣,這裡不多做解釋。

  這麼用起來確實很簡單,但是不能自己設定請求頭,如果要構造請求頭的話可以參考下面的例子:

1 req, _ := http.NewRequest("GET", url, nil)
2 // Set User-Agent
3 req.Header.Add("UserAgent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36")
4 client := &http.Client{}
5 resp, err := client.Do(req)

 

四、解析網頁

1.解析庫選擇

  Go 語言中可以用來解析網頁的庫也是很齊全的,XPath、CSS 選擇器和正則表示式都能用。這裡我用的是 htmlquery,這個庫使用的是 Xpath 選擇器。htmlquery 是用於 HTML 的 XPath 資料提取庫,可通過 XPath 表示式從 HTML 文件中提取資料。Xpath 語法就不提了,畢竟用 Python 寫爬蟲的時候沒少用。

  先說下 htmlquery 的安裝吧,一般會推薦你使用如下命令安裝:

go get github.com/antchfx/htmlquery

  但是你懂的,出於某些原因就下載不下來,怎麼辦呢?對於這種能在 GitHub 上找到的庫直接 clone 到本地就行了,記得要複製到你的 GOAPTH 下。

2.使用 htmlquery

  在使用 htmlquery 這個庫的時候,可能會報錯說缺少 golang.org\x\text,和上面的解決辦法一樣,去 GitHub 上找,然後 clone 下來。

  下面是 htmlquery 中經常使用的方法及相應含義:

func Parse(r io.Reader) (*html.Node, error):  返回給定 Reader 的 HTML 的解析樹。

func Find(top *html.Node, expr string) []*html.Node: 搜尋與指定 XPath 表示式匹配的 html.Node。

func FindOne(top *html.Node, expr string) *html.Node: 搜尋與指定 XPath 表示式匹配的 html.Node,並返回匹配的第一個元素,可簡單理解為 FindOne = Find[0]

func InnerText(n *html.Node) string: 返回物件的開始和結束標記之間的文字。
 
func SelectAttr(n *html.Node, name string) (val string): 返回指定名稱的屬性值。

func OutputHTML(n *html.Node, self bool) string: 返回包含標籤名稱的文字。

   下面是使用 htmlquery 解析網頁的程式碼:

 1 // Used to parse html
 2 func parse(html string) {
 3     // Parse html
 4     root, _ := htmlquery.Parse(strings.NewReader(html))
 5     titleList := htmlquery.Find(root, `//*[@id="post_list"]/div/div[2]/h3/a/text()`)
 6     hrefList := htmlquery.Find(root, `//*[@id="post_list"]/div/div[2]/h3/a/@href`)
 7     authorList := htmlquery.Find(root, `//*[@id="post_list"]/div/div[2]/div/a/text()`)
 8 
 9     // Traverse the result
10     for i := range titleList {
11         blog := BlogInfo{}
12         blog.title = htmlquery.InnerText(titleList[i])
13         blog.href = htmlquery.InnerText(hrefList[i])
14         blog.author = htmlquery.InnerText(authorList[i])
15         fmt.Println(blog)
16     }
17 }

  需要注意的是由於在 Go 語言中不支援使用單引號來表示字串,而要使用反引號“`”和雙引號來表示字串。然後因為 Find() 方法返回的是一個數組,因而需要遍歷其中每一個元素,使用 for 迴圈遍歷即可。在 for 迴圈中使用到的 BlogInfo 是一個結構體,表示一個部落格的基本資訊,定義如下:

1 // Used to record blog information
2 type BlogInfo struct {
3     title string
4     href string
5     author string
6 }

 

五、Go 併發

     在 Go 語言中使用 go 關鍵字開啟一個新的 go 程,也叫 goroutine,開啟成功之後,go 關鍵字後的函式就將在開啟的 goroutine 中執行,並不會阻塞當前程序的執行,所以要用 Go 來寫併發還是很容易的。例如:

 1 baseUrl := "https://www.cnblogs.com/"
 2 for i := 2; i < 4; i ++ {
 3     url := baseUrl + "#p" + strconv.Itoa(i)
 4     // fmt.Println(url)
 5     go request(url)
 6 }
 7 
 8 // Wait for goroutine
 9 time.Sleep(2 * time.Second)
10 request(baseUrl)

  這裡除了在主程序中有一個 request(),還開啟了兩個 go 程來執行 request()。不過要注意的是,一旦主程序結束,其餘 Go 程也會結束,所以我這裡加了一個兩秒鐘的等待時間,用於讓 Go 程先結束。

 

六、體驗總結

  由於我本身才剛開始學習 Go,就還有很多東西沒有學到,所以這個初體驗其實還有很多沒寫到的地方,比如資料儲存,去重問題等等,後面會去多看看 Go 的官方文件。當然了,對我來說,要寫爬蟲的話還是會用 Python 來寫的,不過還是得花時間學習新知識,比如使用 Go 做開發,熟悉掌握 Go 語言就是我的下一目標了。

 

  完整程式碼已上傳到 GitHu