我的Go+语言初体验——(4)零基础学习 Go+ 爬虫
我的Go+語言初體驗——(4)零基礎學習 Go+ 爬蟲
“我的Go+語言初體驗” | 征文活動進行中…
Go+ 語言非常適合編寫爬蟲程序,具有并發機制完善、并發數量大、占用資源少、運行速度快、部署方便的優點。
本文結合官方文檔與 Go 語言的資料,循序漸進介紹 Go+ 爬蟲編程,通過多個完整例程帶你學習和編寫爬蟲程序。
本文全部例程已在 Go+ 環境下進行了測試。
1. 為什么用 Go+ 語言編寫爬蟲
網絡爬蟲的工作原理是通過檢查 web 頁面的 HTML 內容,并基于內容執行某種類型的行動。特別是提取當前頁面的數據,以及通過抓取和分析暴露的鏈接,按照隊列去爬取頁面數據。
Go+ 語言非常適合編寫爬蟲程序,并且具有獨特的優勢:
- 并發機制完善
- 并發數量大
- 占用資源少
- 運行速度快
- 部署方便
2. http.Get 方法實現簡單請求
2.1 http.Get 方法說明
net 包封裝了網絡相關的功能,最常用的是 http 和 url。Go+ 語言可以使用 net/http 包實現請求網頁。
http.Get 方法的基本語法如下:
resp, err := http.Get(“http://example.com/”)
參數:請求的目標 URL
向服務器發送一個 http get 請求,得到 response。
2.2 http.Get 獲得網頁的 html 源文件
我們首先用 http.Get 編寫一個簡單的爬蟲程序。
【例程 1】http.Get 獲得網頁的 html 源文件
// 1. http.Get 獲得網頁的 html 源文件 package main import ("fmt""io/ioutil""net/http" )func main() {resp, _ := http.Get("http://www.baidu.com")defer resp1.Body.Close()contents, _ := ioutil.ReadAll(resp.Body)fmt.Println(string(contents)) }運行【例程1】,可以抓取百度首頁的 html 源文件。
數據太多,看不清楚?沒有關系,我們可以把抓取的數據保存下來。
2.3 異常處理和網頁保存
網絡活動十分復雜,對網站的訪問不一定都會成功,因此需要對爬取過程中的異常進行處理,否則爬蟲在遇到異常時就會因發生運行錯誤。
導致異常的原因主要有:
- 鏈接不上服務器
- 遠程URL不存在
- 無網絡
- 觸發了 HTTPError
我們以 CSDN 熱榜網頁為例,說明異常處理和數據保存,并將爬取的網頁保存在 .vscode 目錄下的 page01.html 文件中。
【例程 2】異常處理和網頁保存
// 2. 抓取網頁,異常處理和數據保存 package main import ("fmt""io/ioutil""net/http""os" )func main() {resp, err := http.Get("https://blog.csdn.net/rank/list")if err != nil {fmt.Printf("%s", err)os.Exit(1)}defer resp.Body.Close()if resp.StatusCode == http.StatusOK {fmt.Println(resp.StatusCode)}f, err := os.OpenFile("csdnPage01.html", os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)if err != nil {panic(err)return}defer f.Close()buf := make([]byte, 1024)for {n, _ := resp.Body.Read(buf)if 0 == n {break}f.WriteString(string(buf[:n]))} }加入異常處理后,可以處理網頁訪問中發生的異常,可以保證從服務器成功獲取網頁。
但是,我們真的抓到了所要的網頁嗎?打開 csdnPage01.html 文件,如下圖所示。
【例程 2】確實成功抓取了 CSDN 熱榜頁面中的一些內容,但關鍵的熱榜文章的具體內容并沒有抓到。沒有關系,我們一步步來嘗試。
3. client.Get 方法實現發起請求
3.1 Client 對象說明
Client 方法是 http 包內部發起請求的組件,可以控制請求的超時、重定向和其它設置。
Client 的定義如下:
type Client struct {Transport RoundTripper // 超時控制管理CheckRedirect func(req *Request, via []*Request) error // 控制重定向Jar CookieJar // 管理 Cookie 的對象Timeout time.Duration // 限制建立連接的時間 }Client 的簡單調用方法與 http.Get 方法類似,例如:
resp, err := client.Get(“http://example.com”)
// 參數:請求的目標 URL
3.2 控制 HTTP 客戶端的頭結構
更加常用地,要在請求的時候設置頭參數、cookie、證書驗證等參數,可以在使用 Client 時,先構造一個 Request 再調用 Client.Do() 方法。
【例程 3】控制 HTTP 客戶端的頭結構
// 3. 控制 HTTP 客戶端的頭結構 package mainimport ("encoding/json""fmt""io/ioutil""net/http""os" )func checkError(err error) {if err != nil {fmt.Printf("%s", err)os.Exit(1)} }func main() {url := "https://blog.csdn.net/youcans" // 生成 urlclient := &http.Client{} // 生成 clientreq, err := http.NewRequest("GET", url, nil) // 提交請求checkError(err) // 異常處理// 自定義Headercookie_str := "your cookie" // 從瀏覽器復制的 cookie 字符串req.Header.Set("Cookie", cookie_str)userAgent_str := "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"req.Header.Set("User-Agent", userAgent_str) // 生成 User-Agentresp, err := client.Do(req) // 處理返回結果checkError(err)defer resp.Body.Close() // 關閉相關鏈接// 狀態碼檢驗 (http.StatusOK=200)if resp.StatusCode == http.StatusOK {fmt.Println(resp.StatusCode)}/*contents, err := ioutil.ReadAll(resp.Body)checkError(err)fmt.Println(string(contents))*/f, err := os.OpenFile("csdnPage02.html", os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)if err != nil {panic(err)return}defer f.Close()buf := make([]byte, 1024)for {n, _ := resp.Body.Read(buf)if 0 == n {break}f.WriteString(string(buf[:n]))} }【例程 3】成功抓取了 CSDN 網站的 youcans 頁面。打開 csdnPage01.html 文件,顯示如下:
3.3 解析網頁數據
對于獲取的網頁源代碼需要進行解析,以得到我們需要的數據。
依據響應的不同類型,可以選擇不同的方法:
-
對于 html 格式的數據,可以選擇正則表達式或者 Css 選擇器獲取所需的內容;
-
對于 json 格式的數據,可以使用 encoding/json 庫對獲取的數據反序列化,獲取所需的內容。
正則表達式是進行模式匹配和文本操縱的的工具。Go+ 語言通過 regexp 包為正則表達式提供支持,采用 RE2 語法,與 Go、Python 語言的正則一致。
【函數說明】func MustCompile(str string) *Regexp
通過 MustCompile 可以創建一個 Regexp 對象
MustCompile 用來解析正則表達式 str 是否合法
如果合法,則返回一個 Regexp 對象;如果不合法,則拋出異常
例如:
rp :=regexp.MustCompile(`<div class="hd">(.*?)</div>`) // 查找并返回以 <div class="hd"> 開頭,以 </div> 結尾的字符串 titleRe := regexp.MustCompile(`<span class="title">(.*?)</span>`) // 查找并返回以 <span class="title"> 開頭,以 </span> 結尾的字符串**【函數說明】func (re *Regexp) FindAllStringSubmatch(s string, n int) string **
在 s 中查找 re 中編譯好的正則表達式,并返回所有匹配的內容
同時返回子表達式匹配的內容
**【函數說明】func (re *Regexp) FindStringSubmatchIndex(s string) []int **
在 s 中查找 re 中編譯好的正則表達式,并返回第一個匹配的位置
同時返回子表達式匹配的位置
【函數說明】func (re *Regexp) FindStringSubmatch(s string) []string
在 s 中查找 re 中編譯好的正則表達式,并返回第一個匹配的內容
同時返回子表達式匹配的內容
【例程 4】解析網頁數據
// 4. 解析網頁數據 package main import ("fmt""io/ioutil""net/http""os""regexp""strconv""strings""time" )// 異常處理 func checkError(err error) {if err != nil {fmt.Printf("%s", err)panic(err)} }// URL 請求 func fetch(url string) string {fmt.Println("Fetch Url", url)client := &http.Client{} // 生成 clientreq, err := http.NewRequest("GET", url, nil) // 提交請求checkError(err)// 自定義 HeaderuserAgent_str := "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; http://www.baidu.com)"req.Header.Set("User-Agent", userAgent_str) // 生成 User-Agentresp, err := client.Do(req) // 處理返回結果checkError(err)defer resp.Body.Close()// 狀態碼檢驗 (http.StatusOK=200)if resp.StatusCode == 200 {body, err := ioutil.ReadAll(resp.Body) // 讀取resp的body內容checkError(err)return string(body)} else {fmt.Printf("%s", err)return ""} }// 解析頁面 func parseUrls(url string, f *os.File) {//func parseUrls(url string) {body := fetch(url)body = strings.Replace(body, "\n", "", -1) // 去除 body 內容中的回車符rp := regexp.MustCompile(`<div class="hd">(.*?)</div>`)titleRe := regexp.MustCompile(`<span class="title">(.*?)</span>`)idRe := regexp.MustCompile(`<a href="https://movie.douban.com/subject/(\d+)/"`)items := rp.FindAllStringSubmatch(body, -1) // 解析符合正則表達式的結果for _, item := range items {idItem := idRe.FindStringSubmatch(item[1])[1] // 找第一個符合的結果 IDtitleItem := titleRe.FindStringSubmatch(item[1])[1] // 找第一個符合的結果 TITLE//fmt.Println(idItem, titleItem)_, err := f.WriteString(idItem + "\t" + titleItem + "\n")checkError(err)}return }func main() {f, err := os.Create("topMovie.txt") // 創建文件defer f.Close()_, err = f.WriteString("ID\tTitle\n") // 描述字段checkError(err)start := time.Now()// 抓取全部 Top250for i := 0; i < 10; i++ {parseUrls("https://movie.douban.com/top250?start="+strconv.Itoa(25*i), f)} // 把數字轉成字符串elapsed := time.Since(start)fmt.Printf("Took %s", elapsed) }【例程 4】中的 URL 請求獲取網頁數據的程序內容與【例程3】的方法相同,只是將其封裝為函數 fetch() 以方便使用。
【例程 4】從豆瓣抓取了 Top250 影片的信息,運行后控制臺顯示內容為:
Fetch Url https://movie.douban.com/top250?start=0
Fetch Url https://movie.douban.com/top250?start=25
Fetch Url https://movie.douban.com/top250?start=50
Fetch Url https://movie.douban.com/top250?start=75
Fetch Url https://movie.douban.com/top250?start=100
Fetch Url https://movie.douban.com/top250?start=125
Fetch Url https://movie.douban.com/top250?start=150
Fetch Url https://movie.douban.com/top250?start=175
Fetch Url https://movie.douban.com/top250?start=200
Fetch Url https://movie.douban.com/top250?start=225
文件 topMovie.txt 保存的內容為:
ID Title
1292052 肖申克的救贖
1291546 霸王別姬
1292720 阿甘正傳
…
1292528 猜火車
1307394 千年女優
說明:【例程4】參考了資深工程師 【Golang編程】的文章:用Golang寫爬蟲(一),特此致謝!作者進行了改編,并在 Go+ 環境進行了測試。
3. 總結
Go+ 語言非常適合編寫爬蟲程序,這也是 Go+ 的優勢領域。
本文結合官方文檔與 Go 語言的資料,循序漸進介紹 Go+ 爬蟲編程,通過多個完整例程帶你學習和編寫爬蟲程序。
本文中的全部例程都已在 Go+ 環境下進行調試和運行測試。
高并發是 Go+ 的重要機制和優點,在爬蟲中也能非常有效,我們將在后續文中進行學習。
【本節完】
版權聲明:
原創作品,轉載必須標注原文鏈接:(https://blog.csdn.net/youcans/article/details/121644252)
【例程4】參考了資深工程師 “Golang編程”的文章:用Golang寫爬蟲(一),作者進行了改編,特此致謝。
Copyright 2021 youcans, XUPT
Crated:2021-11-30
歡迎關注『我的Go+語言初體驗』系列,持續更新中…
我的Go+語言初體驗——(1)超詳細安裝教程
我的Go+語言初體驗——(2) IDE 詳細安裝教程
我的Go+語言初體驗——(3)Go+ 數據類型
我的Go+語言初體驗——(4)零基礎學習 Go+ 爬蟲
“我的Go+語言初體驗” | 征文活動進行中…
總結
以上是生活随笔為你收集整理的我的Go+语言初体验——(4)零基础学习 Go+ 爬虫的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 只可顺守不可逆取书法_闲章不“闲”
- 下一篇: java web 开发之写在前面(0)