Golang原生爬蟲 簡單爬蟲實現 不依賴第三方包庫 方便理解技術原理 (一)

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

探索技術的路上本應該自己造輪子,即使市面上有再多的選擇,自己動手嘗試也是必要的,第一次嘗試必然會問題眾多,但你不覺得解決他是一件很有成就感的事情嗎,這樣才能帶給你更大的進步和更深刻的領悟。

如果沒有寫過的並感興趣的不妨一起來實現一下這個簡單的爬蟲。

其實用golang實現爬蟲是很簡單是事情,但也分情況,我們這次的文章就分享一種最簡單的爬蟲實現方式,用到的官方庫如下:

import (      "fmt"      "io"      "io/ioutil"      "net/http"      "os"      "regexp"      "strconv"      "strings"      "time"  )  

如果你能單單通過這些庫就想到該怎麼做了,那你就很棒棒了。

為了讓程式能一直運行下去,我們首先要有一個源網頁,然後不斷爬抓記錄新的連結,記錄的手段有很多,比如存在資料庫、通過redis緩衝、存在文字檔,最簡單的應該就是存在資料庫了,這個看你們的技術偏向了。我打算把爬來的連結儲存在文字檔裡。

首先,瞭解自己爬抓的目標,我準備爬取所有的Golang相關答疑或者文章,然後翻來覆去很多網站都感覺不適合做源網址,然後靈機一動,百度一下

然後就用這樣作為源網址吧:百度一下 - Golang實現

有了源網址,那下面的事情只要捋順就好辦了。首先我們為了抓取到連結,需要一個Regex

var (      regHref       = `((ht|f)tps?)://[w]{0,3}.baidu.com/link\?[a-zA-z=0-9-\s]*`  )  

因為這個Regex我們後面可能會複用,所以可以存到一個全域變數裡。

一個爬蟲如果不限制分秒爬抓次數,那你的網路肯定會受不了,如果電腦配置不行的話,電腦也會掛掉,所以我們需要寫一個計時器,golang已經提供了計時器的包 => time

func Timer() {      t := time.NewTimer(time.Second * 1)      <-t.C      fmt.Print("\n\n\n執行爬抓\n\n")      Timer()  }  

 

為什麼要寫在一個Timer函數裡?當然是用來調用的 /手動滑稽

因為我們存在兩種情況,第一次爬取或不是第一次爬取的情況是做不同操作的。那要怎麼判斷呢?因為我們的連結是儲存在txt檔案裡的,所以我們只需要去查txt檔案是不是為空白,如果為空白就認為他是第一次執行程式,先訪問源網址,否則就按照檔案裡的連結依次訪問。

代碼如下:

func main() {      if checkFile("./data/", "url.txt").Size() == 0 {          fistStart()          main()      } else {          Timer()      }  }  

那我們先看一下firstStart()函數,稍後再解釋代碼:

func fistStart() {      var num int      url := "http://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=39042058_20_oem_dg&wd=golang%E5%AE%9E%E7%8E%B0&oq=golang%2520%25E5%2588%25A0%25E9%2599%25A4%25E6%2595%25B0%25E7%25BB%2584&rsv_pq=d9be28ec0002df1b&rsv_t=8017GWpSLPhDmKilZQ1StC04EVpUAeLEP90NIm%2Bk5pRh5R9o57NHMO8Gaxm1TtSOo%2FvtJj%2B98%2Fsc&rqlang=cn&rsv_enter=1&inputT=3474&rsv_sug3=16&rsv_sug1=11&rsv_sug7=100&rsv_sug2=0&rsv_sug4=4230"      resp, _ := http.Get(url)      defer resp.Body.Close()      body, _ := ioutil.ReadAll(resp.Body)      reg := regexp.MustCompile(`((ht|f)tps?)://[w]{0,3}.baidu.com/link\?[a-zA-z=0-9-\s]*`)      f, _ := os.OpenFile("./data/url.txt", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)      defer f.Close()      for _, d := range reg.FindAllString(string(body), -1) {          ff, _ := os.OpenFile("./data/url.txt", os.O_RDWR, 0666)          file, _ := ioutil.ReadAll(ff)          dd := strings.Split(d, "")          dddd := ""          for _, ddd := range dd {              if ddd == "?" {                  ddd = `\?`              }              dddd += ddd          }          if checkRegexp(string(file), dddd, 0).(string) == "" {              io.WriteString(f, d+"\n")              fmt.Print("\n收集地址:" + d + "\n")              num++          }          // fmt.Print(string(file))          ff.Close()      }      fmt.Print("\n首次收集網路地址:" + strconv.Itoa(len(reg.FindAllString(string(body), -1))) + "\n")      fmt.Print("\n去重後網路地址數:" + strconv.Itoa(num))      fmt.Print("\n\n首次儲存成功!\n")  } 

很抱歉,沒有注釋習慣

 

其實很簡單,就是發起一個get請求,然後你會擷取到byte[]類型的資料,轉換成string類型之後,就是網頁的代碼了。

 

分解一下(瞭解原理的跳過這段):

url := "http://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=39042058_20_oem_dg&wd=golang%E5%AE%9E%E7%8E%B0&oq=golang%2520%25E5%2588%25A0%25E9%2599%25A4%25E6%2595%25B0%25E7%25BB%2584&rsv_pq=d9be28ec0002df1b&rsv_t=8017GWpSLPhDmKilZQ1StC04EVpUAeLEP90NIm%2Bk5pRh5R9o57NHMO8Gaxm1TtSOo%2FvtJj%2B98%2Fsc&rqlang=cn&rsv_enter=1&inputT=3474&rsv_sug3=16&rsv_sug1=11&rsv_sug7=100&rsv_sug2=0&rsv_sug4=4230"  resp, _ := http.Get(url)  defer resp.Body.Close()  body, _ := ioutil.ReadAll(resp.Body)  reg := regexp.MustCompile(`((ht|f)tps?)://[w]{0,3}.baidu.com/link\?[a-zA-z=0-9-\s]*`)  f, _ := os.OpenFile("./data/url.txt", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)  defer f.Close()  

 

這段主要是發起一個get網路請求,然後把請求到的byte資料轉成stirng類型的資料,跳過正則擷取匹配連結擷取一個連結數組(不過分贅述,如果還不懂http請求可以另尋百度)

for _, d := range reg.FindAllString(string(body), -1) {      ff, _ := os.OpenFile("./data/url.txt", os.O_RDWR, 0666)      file, _ := ioutil.ReadAll(ff)      dd := strings.Split(d, "")      dddd := ""      for _, ddd := range dd {          if ddd == "?" {              ddd = `\?`          }          dddd += ddd      }      if checkRegexp(string(file), dddd, 0).(string) == "" {          io.WriteString(f, d+"\n")          fmt.Print("\n收集地址:" + d + "\n")          num++      }      // fmt.Print(string(file))      ff.Close()  }  

通過迴圈數組,首先對連結裡的特殊符號做特出處理,然後通過checkRegexp函數做查重,就是防止有多個重複的連結記錄導致浪費資源,最後存入txt檔案。

checkRegexp函數:

func checkRegexp(cont string, reg string, style int) (result interface{}) {      check := regexp.MustCompile(reg)      switch style {      case 0:          result = check.FindString(cont)      case 1:          result = check.FindAllString(cont, -1)      default:          result = check.FindAll([]byte(cont), -1)      }      return  }  

這裡,程式的首次執行已經完成,並可以成功記錄爬取的連結了。程式執行如下:

 

下一篇繼續講如何通過這些記錄的連結過濾無用爬取有效內容,如果在上面的代碼中,還有疑問或發現驚天大Bug可以加我QQ625366394一起解決

 

首發文地址:https://blog.csdn.net/superwebmaster/article/details/80319502

 

附代碼執行個體下載地址:https://download.csdn.net/download/superwebmaster/10415730

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.