最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

go語言實現(xiàn)并發(fā)網(wǎng)絡(luò)爬蟲的示例代碼

 更新時間:2023年03月28日 14:15:20   作者:q56731523  
本文主要介紹了go語言實現(xiàn)并發(fā)網(wǎng)絡(luò)爬蟲的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

go語言做爬蟲也是很少嘗試,首先我的思路是看一下爬蟲的串行實現(xiàn),然后通過兩個并發(fā)實現(xiàn):一個使用鎖,另一個使用通道

這里不涉及從頁面中提取URL的邏輯(請查看Go框架colly的內(nèi)容)。網(wǎng)絡(luò)抓取只是作為一個例子來考察Go的并發(fā)性。

我們想從我們的起始頁中提取所有的URL,將這些URL保存到一個列表中,然后對列表中的每個URL做同樣的處理。頁面的圖很可能是循環(huán)的,所以我們需要記住哪些頁面已經(jīng)經(jīng)歷了這個過程(或者在使用并發(fā)時,處于這個過程的中間)。

在這里插入圖片描述

串行爬蟲首先檢查我們是否已經(jīng)在獲取地圖中獲取了該頁面。如果我們沒有,那么它就在頁面上找到的每個URL上調(diào)用自己。注意:map 在Go中是引用類型,所以每次調(diào)用都會得到相同的 map。

func Serial(url string, fetcher Fetcher, fetched map[string]bool) {
    if fetched[url] {
        return
    }
    fetched[url] = true
    urls, err := fetcher.Fetch(url)
    if err != nil {
        return
    }
    for _, u := range urls {
        Serial(u, fetcher, fetched)
    }
    return
}
func main() {
    Serial(<page>, fetcher, make(map[string]bool))
}

fetcher將包含提取URLs到列表中的邏輯(也可以對頁面的內(nèi)容做一些處理)。這個實現(xiàn)不是本講的重點。

由于網(wǎng)絡(luò)速度很慢,我們可以使用并發(fā)性來加快這個速度。為了實現(xiàn)這一點,我們需要使用鎖(在讀/寫時鎖定已經(jīng)獲取的頁面地圖)和 waitgroup(等待所有的goroutine完成)。

已經(jīng)獲取的頁面的 map 只能由持有鎖的線程訪問,因為我們不希望多個線程開始處理同一個URL。如果在一個線程的讀和寫之間,另一個線程在第一個線程更新之前從 map 上得到了相同的讀數(shù),這就可能發(fā)生。

我們定義了fetchState結(jié)構(gòu),將 map 和鎖組合在一起,并定義了一個方法來初始化它。

爬蟲程序的開始是一樣的,檢查我們是否已經(jīng)獲取了URL,但這次使用sync.Mutex來鎖定 map,如前所述。然后,對于頁面上發(fā)現(xiàn)的每個URL,我們在一個新的goroutine中啟動相同的函數(shù)。在啟動之前,我們將WaitGroup的計數(shù)器增加1,done.Wait()在退出之前等待所有的抓取工作完成。

func ConcurrentMutex(url string, fetcher Fetcher, f *fetchState) {
    f.mu.Lock()
    already := f.fetched[url]
    f.fetched[url] = true
    f.mu.Unlock()
    if already {
        return
    }
    urls, err := fetcher.Fetch(url)
    if err != nil {
        return
    }
    var done sync.WaitGroup
    for _, u := range urls {
        done.Add(1)
        go func(u string) {
            defer done.Done()
            ConcurrentMutex(u, fetcher, f)
        }(u)
    }
    done.Wait()
    return
}
type fetchState struct {
    mu      sync.Mutex
    fetched map[string]bool
}
func makeState() *fetchState {
    f := &fetchState{}
    f.fetched = make(map[string]bool)
    return f
}
func main() {
    ConcurrentMutex(<page>, fetcher, makeState())
}

注意:

[1] done.Done()的調(diào)用被推遲了,以防我們在其中一個調(diào)用中出現(xiàn)錯誤,在這種情況下,我們?nèi)匀灰f減WaitGroup的計數(shù)器。

[2] 這段代碼的一個問題是,我們沒有限制線程的數(shù)量。但值得一提的是,goroutines比其他語言的線程更輕量級,并且由Go運行時管理,系統(tǒng)調(diào)用更少。

[3] 我們把字符串u傳給立即函數(shù),以便制作一個URL的副本,然后才把它送到goroutine,因為變量u在外層for循環(huán)中發(fā)生了變化。要理解這樣做的必要性,一個更簡單的例子是,在沒有WaitGroup的情況下。

func checkThisOut() {
  s := "abc"
  sec := time.Second
  go func() {time.Sleep(sec); fmt.Printf("s = %v\n", s)}()
  go func(u string) {time.Sleep(sec); fmt.Printf("u = %v\n", u)}(s)
  s = "def"
  time.Sleep(2 * sec)
}
// this prints out: u = abc, s = def

[4] 我們可以運行內(nèi)置的數(shù)據(jù)競賽檢測器,通過運行g(shù)o run -race .來幫助檢測競賽條件。它在這個例子中非常有效。

下一個并發(fā)版本在線程之間完全不共享內(nèi)存!嗯,這并不準確。我們只是不會自己同步訪問共享數(shù)據(jù)。相反,我們使用一個通道在goroutine之間進行通信。

在這個最后的版本中,我們有一個主函數(shù)在主線程上運行。只有這個函數(shù)能看到 map 并從通道中讀取。channel ,像 map 一樣,也是引用類型。所以這里只有一個通道。

在啟動時,我們將第一個URL寫到通道上。這是在一個goroutine中完成的,因為向一個沒有緩沖的通道的寫入會導致goroutine暫停,直到該值被另一個goroutine讀取。

我們在一個for循環(huán)中從通道中讀取URL的列表(從一個沒有緩沖的通道中讀取也會阻塞)。然后,我們以與之前的實現(xiàn)類似的方式瀏覽該列表。通過使用一個計數(shù)器,一旦沒有更多的工作者,這個循環(huán)就會中斷。

工作者獲取URL的列表,將它們傳遞給通道。如果出現(xiàn)錯誤,會傳遞一個空列表,這樣從通道讀取的for循環(huán)最終會退出(計數(shù)器的設(shè)置方式是,我們等待從每個goroutine讀取一個值)。

func ConcurrentChannel(url string, fetcher Fetcher) {
    ch := make(chan []string)
    go func() {
        ch <- []string{url}
    }()
    master(ch, fetcher)
}
func master(ch chan []string, fetcher Fetcher) {
    n := 1
    fetched := make(map[string]bool)
    for urls := range ch {
        for _, u := range urls {
            if fetched[u] == false {
                fetched[u] = true
                n += 1
                go worker(u, ch, fetcher)
            }
        }
        n -= 1
        if n == 0 {
            break
        }
    }
}
func worker(url string, ch chan []string, fetcher Fetcher) {
    urls, err := fetcher.Fetch(url)
    if err != nil {
        ch <- []string{}
    } else {
        ch <- urls
    }
}

到此這篇關(guān)于go語言實現(xiàn)并發(fā)網(wǎng)絡(luò)爬蟲的示例代碼的文章就介紹到這了,更多相關(guān)go語言并發(fā)網(wǎng)絡(luò)爬蟲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go Web 編程中的模板庫應(yīng)用指南(超詳細)

    Go Web 編程中的模板庫應(yīng)用指南(超詳細)

    這篇文章主要介紹了Go Web 編程中的模板庫應(yīng)用指南,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • Go語言中的iota關(guān)鍵字的使用

    Go語言中的iota關(guān)鍵字的使用

    這篇文章主要介紹了Go語言中的iota關(guān)鍵字的使用的相關(guān)資料,需要的朋友可以參考下
    2023-08-08
  • 一文帶你掌握Go語言運算符的使用

    一文帶你掌握Go語言運算符的使用

    運算符用于在程序運行時執(zhí)行數(shù)學或邏輯運算。Go 語言內(nèi)置的運算符有:算術(shù)運算符、關(guān)系運算符、邏輯運算符、位運算符、賦值運算符、其他運算符。本文將帶大家詳細了解一下這些運算符的使用,感興趣的可以了解一下
    2022-04-04
  • 詳解Go函數(shù)和方法之間有什么區(qū)別

    詳解Go函數(shù)和方法之間有什么區(qū)別

    這篇文章就簡單和大家聊一聊在Go中函數(shù)與方法之間的區(qū)別,文章通過代碼示例介紹的非常詳細,對我們的學習或工作有一定的幫助,感興趣的小伙伴跟著小編一起來看看吧
    2023-07-07
  • Golang的鎖機制使用及說明

    Golang的鎖機制使用及說明

    這篇文章主要介紹了Golang的鎖機制使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 如何用Go判斷元素是否在切片中

    如何用Go判斷元素是否在切片中

    切片(Slice)是一個擁有相同類型元素的可變長度的序列,下面這篇文章主要給大家介紹了關(guān)于如何用Go判斷元素是否在切片中的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-06-06
  • go語言串口通信的項目實踐

    go語言串口通信的項目實踐

    在Go語言可以使用github.com/tarm/serial包來進行串口通信,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-11-11
  • Golang中大端序和小端序的處理

    Golang中大端序和小端序的處理

    大端序和小端序是描述多字節(jié)數(shù)據(jù)在內(nèi)存中存儲順序的術(shù)語,本文主要介紹了Golang中大端序和小端序的處理,具有一定的參考價值,感興趣的可以了解一下
    2025-02-02
  • Golang實現(xiàn)短網(wǎng)址/短鏈服務(wù)的開發(fā)筆記分享

    Golang實現(xiàn)短網(wǎng)址/短鏈服務(wù)的開發(fā)筆記分享

    這篇文章主要為大家詳細介紹了如何使用Golang實現(xiàn)短網(wǎng)址/短鏈服務(wù),文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以了解一下
    2023-05-05
  • Go語言中循環(huán)Loop的用法介紹

    Go語言中循環(huán)Loop的用法介紹

    這篇文章介紹了Go語言中循環(huán)Loop的用法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07

最新評論

山阴县| 唐山市| 咸阳市| 高平市| 黎平县| 晋中市| 仁化县| 玛多县| 浠水县| 丹凤县| 铜鼓县| 大城县| 左云县| 上饶市| 昭平县| 洛川县| 沧源| 广水市| 西乡县| 石家庄市| 东莞市| 皮山县| 三原县| 德庆县| 杭州市| 通道| 怀仁县| 景泰县| 克东县| 寿阳县| 金塔县| 沐川县| 健康| 汉沽区| 灌南县| 浦东新区| 兴安盟| 汝城县| 于田县| 长阳| 临安市|