go語言resty庫并發(fā)爬取bing大圖實戰(zhàn)

前言

最近開始好好在看go語言废酷,拖延很久終于把小伙伴給的《Go語言實戰(zhàn)》書看完了抹缕,但是還沒實際上手操作過,想著當年學python的時候也是從爬蟲實戰(zhàn)開始的趴俘,那就先寫個go的并發(fā)爬蟲來作為實戰(zhàn)吧寥闪。

使用的爬蟲庫

我這里使用了restyhttp請求庫疲憋,而非go語言內(nèi)置的請求庫缚柳,因為resty庫封裝更多,對參數(shù)設(shè)置和文件下載十分方便

使用的html解析

我習慣使用的xpath方式進行html解析喂击,因此選擇使用htmlquery庫翰绊,如果你喜歡css解析方式也可以使用goquery

使用的并發(fā)控制

我這里使用的sync.WaitGroup進行的并發(fā)控制,也可以改造為channel的方式貌似更好谐檀?

爬取目標及思路

必應的每日一圖很適合作為練手項目桐猬,涉及有分頁溃肪、有html內(nèi)容提取惫撰、有圖片下載到本地厨钻,正好練手坚嗜。本次爬蟲目標是并發(fā)的方式抽取每頁大圖url地址苍蔬,并且下載大圖到本地银室。

  • 主頁地址: https://bing.ioliu.cn/
  • 爬取思路解析
    本地爬取需要提取大圖url地址和保存的文件名,通過后續(xù)分析辜荠,大圖地址只需要將后綴改為?force=download即是直接下載的地址伯病,因此后續(xù)需要處理一下抽取到的url午笛。每個page頁面的查詢參數(shù)是?p=1結(jié)構(gòu)药磺。
    因此爬取思路很簡單:請求每個page頁面 -> 抽取該頁面下所有大圖下載url -> 請求每個下載url并將圖片保存到本地癌佩。
    image.png

代碼部分

記錄下代碼的設(shè)計思路,由于是初學我碟,有不足地方還屬正常~
使用了waitGroup的方式控制并發(fā)同步,代碼內(nèi)對每一頁的抽取及每個大圖的下載都使用了goruntine并發(fā)的方式掸冤,但并沒有對并發(fā)數(shù)量進行控制厘托,容易被封ip~ 后續(xù)需要改進。
這里可以改造為使用channel通道進行并發(fā)控制貌似更好一些稿湿。

package main

import (
    "github.com/antchfx/htmlquery"
    "github.com/go-resty/resty/v2"
    "golang.org/x/net/html"
    "log"
    "strconv"
    "strings"
    "sync"
    "time"
)

// bing圖片主頁
const restyBaseUrl = "https://bing.ioliu.cn/"
// 下載目錄
const restyDownloadPath = "/Users/my/go/src/goProjects/downloads"

func main() {
    // 使用waitGroup控制并發(fā)同步催烘,需要在適宜的位置使用Add和Done方法
    var wg sync.WaitGroup
    // 爬取三頁測試,多了要出事
    for page := 1; page <= 3; page++ {
        wg.Add(1)
        go getPageImage(page, &wg)
    }
    wg.Wait()
    log.Println("下載完成")
}

// 大圖結(jié)構(gòu)化信息:下載地址及保存文件名
type imageStruct struct{
    url string
    name string
}

// 大圖結(jié)構(gòu)綁定方法缎罢,下載到本地,使用resty包可以很簡單的定義重試策略考杉、文件下載目錄及文件名策精,無需手動操作os,很棒
func (image imageStruct) download(waitGroup *sync.WaitGroup)  {
    defer waitGroup.Done()
    filename := image.name + ".jpg"
    client := resty.New()
    // set retry
    client.
        SetRetryCount(3).
        // Default is 100 milliseconds.
        SetRetryWaitTime(5 * time.Second).
        // Default is 2 seconds.
        SetRetryMaxWaitTime(20 * time.Second)
    // request and save file to path
    client.SetOutputDirectory(restyDownloadPath)
    _, err := client.R().
        SetHeader("Referer", "https://bing.ioliu.cn/").
        SetHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36").
        SetOutput(filename).
        Get(image.url)
    if err != nil {
        log.Printf("下載圖片失敵缣摹:%s,err:%s", image.name, err)
        return
    }
    log.Printf("下載圖片成功:%s", image.name)
}

func getPageImage(page int, waitGroup *sync.WaitGroup) {
    // 請求分頁頁面并解析出大圖詳情結(jié)構(gòu)
    defer waitGroup.Done()
    
    // 使用resty包進行http請求咽袜,更方便的構(gòu)造請求頭參數(shù)和查詢參數(shù)
    client := resty.New()
    resp, err := client.R().
        SetQueryParams(map[string]string{
            "p": strconv.Itoa(page),
        }).
        SetHeader("Referer", "https://bing.ioliu.cn/").
        SetHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36").
        Get(restyBaseUrl)
    if err != nil {
        log.Fatal(err)
        return
    }
    // 使用htmlquery包進行html元素解析,使用xpath的方式抽取大圖item信息
    doc, qErr := htmlquery.Parse(strings.NewReader(resp.String()))
    if qErr != nil {
        log.Fatal(qErr)
    }
    // xpath 匹配響應中所有圖片元素,并逐個提取并發(fā)下載
    imageItems := htmlquery.Find(doc, "http://div[@class='item']/div[@class='card progressive']")
    for _, item := range imageItems {
        filename, bigImageUrl := extractImageInfo(item)
        // 保存大圖結(jié)構(gòu)化信息
        img := imageStruct{
            url: bigImageUrl,
            name: filename,
        }
        log.Printf("開始下載:%s", img.name)
        // 并發(fā)下載圖片,控制時間間隔
        waitGroup.Add(1)
        // 并發(fā)調(diào)用大圖結(jié)構(gòu)化方法,下載大圖到本地并命名
        go img.download(waitGroup)
    }

}

// 從大圖item元素中抽取下載地址及圖片名
func extractImageInfo(item *html.Node) (string, string){
    //抽取圖片信息
    extractImageUrlElem := htmlquery.FindOne(item, "./a")
    nameElem := htmlquery.FindOne(item, "./div[@class='description']/h3")
    name := htmlquery.InnerText(nameElem)
    extractImageUrl := htmlquery.SelectAttr(extractImageUrlElem, "href")

    filename := strings.Split(name, "(")[0]
    extractImageUrlRes := strings.Split(extractImageUrl, "?")[0]
    bigImageUrl := "https://bing.ioliu.cn/" + extractImageUrlRes + "?force=download"
    return filename, bigImageUrl
}

效果展示

我的該go文件名為crawl_bing.go
因此在當前目錄下執(zhí)行go run crawl_bing.go即可執(zhí)行,執(zhí)行效果如下:


image.png

我感覺代碼里面有一些問題兄一,因為實際文件目錄很快就已經(jīng)下載完成到本地了造壮,但是日志卻在緩慢打印,看起來就像下載耗時一樣,后面再看看什么問題。昆汹。。

感受

真的學習新語言和框架要自己去實現(xiàn)去敲才行,只看是沒有用的捅彻,加油贤旷。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末韧衣,一起剝皮案震驚了整個濱河市勃蜘,隨后出現(xiàn)的幾起案子缭贡,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荒勇,死亡現(xiàn)場離奇詭異窿凤,居然都是意外死亡窗轩,警方通過查閱死者的電腦和手機仓洼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門某残,熙熙樓的掌柜王于貴愁眉苦臉地迎上來壮虫,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵胯舷,是天一觀的道長逃顶。 經(jīng)常有香客問我妙蔗,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮劲适,結(jié)果婚禮上愕贡,老公的妹妹穿的比我還像新娘诫钓。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布叫编。 她就那樣靜靜地躺著霞篡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上救氯,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天准谚,我揣著相機與錄音哲戚,去河邊找鬼脆炎。 笑死癞松,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幔崖,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼忙迁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤著摔,失蹤者是張志新(化名)和其女友劉穎恩掷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氮凝,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年浆劲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丙躏。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡谈秫,死狀恐怖拟烫,靈堂內(nèi)的尸體忽然破棺而出硕淑,到底是詐尸還是另有隱情拇囊,我是刑警寧澤纠永,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布辜纲,位于F島的核電站固翰,受9級特大地震影響狼纬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骂际,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一疗琉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧歉铝,春花似錦没炒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至先匪,卻和暖如春种吸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呀非。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工坚俗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留镜盯,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓猖败,卻偏偏與公主長得像速缆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子恩闻,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

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