New一個golang爬蟲

剛好七八雙月結(jié)束勺远,工作整理完畢橙喘,下個雙月OKR還沒開始。做久了IOS開發(fā)也來擴展下領(lǐng)域胶逢,抽空幾天學(xué)了下Golang厅瞎,實現(xiàn)一個爬蟲。

一初坠、知識要點

1和簸、爬蟲

1.1 工作方式

傳統(tǒng)爬蟲從一個或若干初始網(wǎng)頁的URL開始,獲得初始網(wǎng)頁上的URL碟刺,在抓取網(wǎng)頁的過程中锁保,不斷從當(dāng)前頁面上抽取新的URL放入隊列,直到滿足系統(tǒng)的一定停止條件。聚焦爬蟲的工作流程較為復(fù)雜,需要根據(jù)一定的網(wǎng)頁分析算法過濾與主題無關(guān)的鏈接爽柒,保留有用的鏈接并將其放入等待抓取的URL隊列吴菠。然后,它將根據(jù)一定的搜索策略從隊列中選擇下一步要抓取的網(wǎng)頁URL浩村,并重復(fù)上述過程做葵,直到達(dá)到系統(tǒng)的某一條件時停止。另外心墅,所有被爬蟲抓取的網(wǎng)頁將會被系統(tǒng)存貯蜂挪,進(jìn)行一定的分析、過濾嗓化,并建立索引,以便之后的查詢和檢索谬哀;對于聚焦爬蟲來說刺覆,這一過程所得到的分析結(jié)果還可能對以后的抓取過程給出反饋和指導(dǎo)。

1.2 分類

  • 全網(wǎng)爬蟲史煎,爬行對象從一些種子 URL 擴充到整個 Web谦屑,主要為門戶站點搜索引擎和大型 Web 服務(wù)提供商采集數(shù)據(jù)。
  • 聚焦網(wǎng)絡(luò)爬蟲篇梭,是指選擇性地爬行那些與預(yù)先定義好的主題相關(guān)頁面的網(wǎng)絡(luò)爬蟲氢橙。
  • 增量式網(wǎng)絡(luò)爬蟲,是指對已下載網(wǎng)頁采取增量式更新和只爬行新產(chǎn)生的或者已經(jīng)發(fā)生變化網(wǎng)頁的爬蟲恬偷,它能夠在一定程度上保證所爬行的頁面是盡可能新的頁面悍手。
  • Deep Web 爬蟲,表層網(wǎng)頁是指傳統(tǒng)搜索引擎可以索引的頁面袍患,以超鏈接可以到達(dá)的靜態(tài)網(wǎng)頁為主構(gòu)成的Web頁面坦康。Deep Web 是那些大部分內(nèi)容不能通過靜態(tài)鏈接獲取的、隱藏在搜索表單后的诡延,只有用戶提交一些關(guān)鍵詞才能獲得的 Web 頁面滞欠。

1.3爬蟲算法

  • 深度優(yōu)先策略

    其基本方法是按照深度由低到高的順序,依次訪問下一級網(wǎng)頁鏈接肆良,直到不能再深入為止筛璧。 爬蟲在完成一個爬行分支后返回到上一鏈接節(jié)點進(jìn)一步搜索其它鏈接。 當(dāng)所有鏈接遍歷完后惹恃,爬行任務(wù)結(jié)束夭谤。 這種策略比較適合垂直搜索或站內(nèi)搜索, 但爬行頁面內(nèi)容層次較深的站點時會造成資源的巨大浪費座舍。

  • 廣度優(yōu)先策略

    此策略按照網(wǎng)頁內(nèi)容目錄層次深淺來爬行頁面沮翔,處于較淺目錄層次的頁面首先被爬行。 當(dāng)同一層次中的頁面爬行完畢后,爬蟲再深入下一層繼續(xù)爬行采蚀。 這種策略能夠有效控制頁面的爬行深度疲牵,避免遇到一個無窮深層分支時無法結(jié)束爬行的問題,實現(xiàn)方便榆鼠,無需存儲大量中間節(jié)點纲爸,不足之處在于需較長時間才能爬行到目錄層次較深的頁面

2、golang

2.1 語法學(xué)習(xí)

  • 為了讓學(xué)習(xí)更加快速妆够,想要優(yōu)先上手识啦,而不是沉浸在大量語法里面,找了一遍基礎(chǔ)的語法文檔神妹,直接全讀整體語法颓哮,先有個基礎(chǔ)但是全面的認(rèn)識。
    語法教程鏈接

  • 然后直接手一本web教程書籍鸵荠,此本書籍是開源的冕茅,在github上有1.78萬star,5年前就開始書寫蛹找,一直被追捧和使用姨伤。書籍鏈接

2.2 環(huán)境安裝

1.1 在MacOSX上安裝

  • 下載地址
  • 源碼包:go1.4.linux-amd64.tar.gz。
  • 將下載的源碼包解壓至 /usr/local目錄庸疾。
tar -C /usr/local -xzf go1.4.linux-amd64.tar.gz

  • 將 /usr/local/go/bin 目錄添加至PATH環(huán)境變量:
export PATH=$PATH:/usr/local/go/bin

  • 注意:MAC 系統(tǒng)下你可以使用 .pkg 結(jié)尾的安裝包直接雙擊來完成安裝乍楚,安裝目錄在 /usr/local/go/ 下。

1.2 其他方式

參考鏈接

二届慈、代碼實現(xiàn)

先確立一個小目標(biāo)徒溪,就是我們要爬取的網(wǎng)頁的數(shù)據(jù)源是什么。一直覺得國內(nèi)的大學(xué)排名爭議比較有趣拧篮,TOP2的兩所词渤,但是TOP5的有8所,TOP10的有20所串绩,哈哈缺虐,所以來爬個大學(xué)排行榜玩玩吧。

1礁凡、網(wǎng)頁抓取

1.1 定義一個學(xué)校


type SchoolObj struct {
    rankTypeName string
    RankIndex int
    SchoolName string
    EnrollOrder string
    StarLevel string
    LocationName string
    SchoolType  string
    UrlAddress string
    SchoolTags []string
}

1.2 單頁面html解析

  • 引入go語言的http函數(shù)包和上面定義的學(xué)校結(jié)構(gòu)題

  • 發(fā)起一個網(wǎng)頁請求返回高氮,go語言會返回網(wǎng)頁的<html>以下全部的html格式字符串

  • 如何從這些字符串中遍歷查找和解析出我們需要的學(xué)校排名字段?

因為有過前端開發(fā)的經(jīng)驗顷牌,我自然而然想到剪芍,使用CSS選擇器會比直接使用遍歷算法來得高效,有CSS的選擇規(guī)則窟蓝,我可以批量規(guī)律的獲取和處理HTML的DOM結(jié)構(gòu)數(shù)據(jù)罪裹。端開發(fā)中的jQuery提供了方便的操作 DOM 的 API。使用 Go 語言做服務(wù)器端開發(fā),有時候需要解析 HTML 文件状共,比如抓取網(wǎng)站內(nèi)容套耕、寫一個爬蟲等。這時候如果有一個類似 jQuery 的庫可以使用峡继,操作 DOM 會很方便冯袍,而且,上手也會很快碾牌。果然康愤,還真有這樣的工具,此處推薦一個GitHub的開源框架 --- Goquery 舶吗。

A征冷、使用介紹:

goquery定義了一個Document結(jié)構(gòu),直接對應(yīng)網(wǎng)頁Javascript的Document節(jié)點誓琼,通過一個NewDocument方法资盅,傳入?yún)?shù)地址為網(wǎng)頁的url地址,直接生產(chǎn)一個虛擬的go語言上的dom踊赠。

type Document struct {
    *Selection
    Url      *url.URL
    rootNode *html.Node
}

func NewDocument(url string) (*Document, error) {
    // Load the URL
    res, e := http.Get(url)
    if e != nil {
        return nil, e
    }
    return NewDocumentFromResponse(res)
}


Document有定義find方法,方法的使用和JQuery里面一直每庆,傳入目標(biāo)字符串的css選擇器即可筐带。通過對Document執(zhí)行find查找方法,獲得全部學(xué)校目標(biāo)的字符串?dāng)?shù)組缤灵。

doc.Find(".bangTable table tr")

這里的選擇器怎么來的呢伦籍,我們在chrome里面打開url地址,找到我們想要收集的數(shù)據(jù)排名腮出,右鍵打開審查元素帖鸦,可以看到HTML的選擇器名稱。這里需要有一點CSS基礎(chǔ)胚嘲,因為有的選擇器不是直接唯一的作儿,需要自己去判斷,怎樣的選擇器組合才能準(zhǔn)確的拿到想要的目標(biāo)字符串馋劈。

Document有定義each方法攻锰,用于遍歷數(shù)組,也就是各個大學(xué)所對應(yīng)的dom節(jié)點妓雾。在each方法中繼續(xù)使用查找方法娶吞,并最后獲得想要的字符串。

每一個dom對應(yīng)一個SchoolStruct械姻,新建并賦值妒蛇,放入數(shù)組中返回。

B、代碼如下:

import (
    "github.com/PuerkitoBio/goquery"
    "SchoolReptile/struct"
    "net/http"
)

func GaokaoquanRank(urlAddress string) []SchoolStruct.SchoolObj {

    var array [] SchoolStruct.SchoolObj

    doc, err := goquery.NewDocument(urlAddress)
    if err != nil {
        log.Fatal(err)
    }

    // Find the review items
    doc.Find(".bangTable table tr").Each(func(i int, s *goquery.Selection) {
        // For each item found, get the band and title
        var obj SchoolStruct.SchoolObj
        obj.RankIndex = s.Find(".t1 span").Text()
        obj.SchoolName = s.Find(".t2 a").Text()
        obj.UrlAddress ,_ = s.Find(".t2 a").Attr("href")
        obj.LocationName = s.Find(".t3").Text()
        obj.SchoolType = s.Find(".t4").Text()
        obj.StarLevel = s.Find(".t5").Text()
        obj.EnrollOrder = "本科第一批"
        array = append(array, obj)

    })

    return array
}

2绣夺、接口請求

我們再爬去數(shù)據(jù)的時候吏奸,一般都能直接抓取網(wǎng)頁數(shù)據(jù),但是有的數(shù)據(jù)在第一頁炳輝展示出來乐导,需要有點擊操作苦丁,比如加載更多。此處的大學(xué)排行有200位物臂,第一頁請求只有20位旺拉,這時候就會發(fā)現(xiàn),接口請求的方便棵磷。
有的網(wǎng)頁在接口上做了cookie校驗蛾狗,摸清別人的請求規(guī)則,才能正確模擬出請求獲得返回數(shù)據(jù)仪媒。

我們此處拿樂學(xué)高考作文例子沉桌,獲取各個類型的大學(xué)排行榜。通過charles代理算吩,我們獲得請求的各類參數(shù)留凭。

  • 拼接請求url
url := LexueHost+"/college/ranking?page="+pageStr+"&rank_type="+rankObj.RankType+"&page_size=15"

  • 發(fā)送HTTP請求,獲取返回

網(wǎng)絡(luò)請求返回的是一個字符串結(jié)構(gòu)的數(shù)據(jù)偎巢,我們需要把它映射成map結(jié)構(gòu)好獲取key對應(yīng)的value值蔼夜。

這里推薦一個go語言在json解析上的一個開源庫Simplejson,將返回的數(shù)據(jù)進(jìn)行JSON結(jié)構(gòu)化压昼,然后通過get方法可以直接獲得對應(yīng)的參數(shù)值求冷。

defer resp.Body.Close()

data, err := ioutil.ReadAll(resp.Body)

jsonBody,err := simplejson.NewJson(data)

schoolJsonArray,err := jsonBody.Get("schools").Array()
    
  • 多頁請求使用遞歸的方式,不斷改變get請求的pageStr參數(shù)窍霞,pageindex ++ 匠题,當(dāng)判斷請求返回的json為空的時候,則說明接口請求已經(jīng)到到了最后一頁但金,跳出遞歸
var nextArray [] SchoolStruct.SchoolObj
nextArray = LexueRankEachList(rankObj,pageIndex)

B韭山、代碼如下:

import (
    "SchoolReptile/struct"
    "net/http"
    "io/ioutil"
    "fmt"
    "bytes"
    "encoding/json"
    "strings"
    "github.com/bitly/go-simplejson"
    "strconv"
)

func LexueRankEachList(rankObj SchoolStruct.RankTypeObj,pageIndex int ) []SchoolStruct.SchoolObj {

    pageStr := strconv.Itoa(pageIndex)

    url := LexueHost+"/college/ranking?page="+pageStr+"&rank_type="+rankObj.RankType+"&page_size=15"

    resp, err := http.Get(url)
    if err != nil {
        // handle error
    }

    defer resp.Body.Close()

    data, err := ioutil.ReadAll(resp.Body)

    jsonBody,err := simplejson.NewJson(data)

    schoolJsonArray,err := jsonBody.Get("schools").Array()

    var array [] SchoolStruct.SchoolObj

    if len(schoolJsonArray) <= 0 {
        println("請求到頭了")
        return array
    }

    for i,_ := range schoolJsonArray {
        schoolJson := jsonBody.Get("schools").GetIndex(i)
        var obj SchoolStruct.SchoolObj
        obj.RankIndex = strconv.Itoa(schoolJson.Get("school_rank").MustInt())
        obj.SchoolName = schoolJson.Get("school_name").MustString()
        obj.SchoolTags = schoolJson.Get("school_tags").MustStringArray()
        array = append(array, obj)
        println(obj.RankIndex,obj.SchoolName,obj.SchoolTags)
    }

    pageIndex++
    var nextArray [] SchoolStruct.SchoolObj
    nextArray = LexueRankEachList(rankObj,pageIndex)
    if len(nextArray) > 0 {
        for _,obj := range nextArray {
            array = append(array,obj)
        }
    }

    return array

}

3、保存到Excel

前兩部獲得了網(wǎng)絡(luò)數(shù)據(jù)冷溃,并解析生成了對應(yīng)的SchoolStruct數(shù)組掠哥,這個時候我們只需要創(chuàng)建excel邊。遍歷數(shù)組秃诵,把數(shù)組里面的數(shù)據(jù)字段都存入表格即可,git開源庫xlsx能夠讓我們輕松的創(chuàng)建续搀、查找、賦值Excel表菠净。

代碼如下:

func SaveSchoolRank(schoolArray [] SchoolStruct.SchoolObj,excelName string,sheetName string)  {

    var file *xlsx.File
    var sheet *xlsx.Sheet
    var row *xlsx.Row
    var cell *xlsx.Cell
    var err error

    file,err = xlsx.OpenFile(excelName + ".xlsx")

    if err != nil {
        file = xlsx.NewFile()
        sheet,err = file.AddSheet(sheetName)
    } else {
       sheet = file.Sheet[sheetName]
    }

    if err == nil {

        for i := 0; i < len(schoolArray); i++ {
            obj := schoolArray[i]

            row = sheet.AddRow()
            cell = row.AddCell()
            cell.Value = obj.RankIndex

            cell = row.AddCell()
            cell.Value = obj.SchoolName

            cell = row.AddCell()
            cell.Value = obj.StarLevel

            cell = row.AddCell()
            cell.Value = obj.LocationName

            cell = row.AddCell()
            cell.Value = obj.EnrollOrder

            cell = row.AddCell()
            cell.Value = obj.SchoolType

            cell = row.AddCell()
            cell.Value = obj.UrlAddress


            var tagStr string
            for _,value := range obj.SchoolTags {
                tagStr += "+" + value
            }
            cell = row.AddCell()
            cell.Value = tagStr


            if err != nil {
                fmt.Printf(err.Error())
            }
        }

    }

    err = file.Save(excelName + ".xlsx")
    if err != nil {
        fmt.Printf(err.Error())
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末禁舷,一起剝皮案震驚了整個濱河市彪杉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌牵咙,老刑警劉巖派近,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異洁桌,居然都是意外死亡渴丸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門另凌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谱轨,“玉大人,你說我怎么就攤上這事吠谢⊥镣” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵工坊,是天一觀的道長献汗。 經(jīng)常有香客問我,道長王污,這世上最難降的妖魔是什么罢吃? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮昭齐,結(jié)果婚禮上刃麸,老公的妹妹穿的比我還像新娘。我一直安慰自己司浪,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布把沼。 她就那樣靜靜地躺著啊易,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饮睬。 梳的紋絲不亂的頭發(fā)上租谈,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音捆愁,去河邊找鬼割去。 笑死,一個胖子當(dāng)著我的面吹牛昼丑,可吹牛的內(nèi)容都是我干的呻逆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼菩帝,長吁一口氣:“原來是場噩夢啊……” “哼咖城!你這毒婦竟也來了茬腿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤宜雀,失蹤者是張志新(化名)和其女友劉穎切平,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辐董,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蕾额,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年语泽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡刀荒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出骚揍,到底是詐尸還是另有隱情媳拴,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布亥至,位于F島的核電站悼沈,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏姐扮。R本人自食惡果不足惜絮供,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望茶敏。 院中可真熱鬧壤靶,春花似錦、人聲如沸惊搏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恬惯。三九已至向拆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間酪耳,已是汗流浹背浓恳。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留碗暗,地道東北人颈将。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像言疗,于是被迫代替她去往敵國和親晴圾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容