其實早就該寫這一篇博客了,爲何一直沒有寫呢?還不是由於忙不過來(實際上只是由於太懶了)。不過好了,如今終於要開始寫這一篇博客了。在看這篇博客以前,可能須要你對 Go 這門語言有些基本的瞭解,好比基礎語法之類的。話很少說,進入正題。html
在學習一門語言時,第一步就是環境配置了,Go 也不例外,下面就是 Windows 下 Go 開發環境的配置過程了。git
首先你須要下載 Go 的安裝包,能夠打開 Go 語言中文網下載,地址爲:https://studygolang.com/dl。github
下載完成後打開安裝(例如安裝到 E:\Go 目錄),而後配置環境變量,將安裝目錄下的 bin 目錄路徑加入環境變量中。這一步完成後打開命令行,輸入 go version,若出現版本信息則代表配置成功。golang
除了要將 bin 目錄加入到環境變量中,還要配置 GOPATH 和 GOROOT,步驟以下:正則表達式
在用戶變量中新建變量 GOPATH:數組
在系統變量中新建變量 GOROOT:併發
在 IDE 的選擇上,我比較推薦使用 jetbrains 家的 GoLand,功能強大,使用起來也很方便。函數
下載網頁使用的是 Go 中原生的 http 庫,在使用前須要導包,和 Python 同樣用 import 導入便可。若是要發送 GET 請求,能夠直接使用 http 中的 Get() 方法,例如:post
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)
Go 語言中能夠用來解析網頁的庫也是很齊全的,XPath、CSS 選擇器和正則表達式都能用。這裏我用的是 htmlquery,這個庫使用的是 Xpath 選擇器。htmlquery 是用於 HTML 的 XPath 數據提取庫,可經過 XPath 表達式從 HTML 文檔中提取數據。Xpath 語法就不提了,畢竟用 Python 寫爬蟲的時候沒少用。
先說下 htmlquery 的安裝吧,通常會推薦你使用以下命令安裝:
go get github.com/antchfx/htmlquery
可是你懂的,出於某些緣由就下載不下來,怎麼辦呢?對於這種能在 GitHub 上找到的庫直接 clone 到本地就好了,記得要複製到你的 GOAPTH 下。
在使用 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 程,也叫 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 語言就是個人下一目標了。
完整代碼已上傳到 GitHub!