3

我正在浏览 golang 并进行最后的练习,以将网络爬虫更改为并行爬取而不是重复爬取 ( http://tour.golang.org/#73 )。我改变的只是抓取功能。

    var used = make(map[string]bool)

    func Crawl(url string, depth int, fetcher Fetcher) {
        if depth <= 0 {
            return
        }
        body, urls, err := fetcher.Fetch(url)
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Printf("\nfound: %s %q\n\n", url, body)
        for _,u := range urls {
            if used[u] == false {
                used[u] = true
                Crawl(u, depth-1, fetcher)
            }
        }
        return
    }

为了使其并发,我在调用 Crawl 函数之前添加了 go 命令,但程序没有递归调用 Crawl 函数,只找到“ http://golang.org/ ”页面而没有其他页面。

为什么我在函数 Crawl 的调用中添加 go 命令时程序不起作用?

4

2 回答 2

9

问题似乎是,在爬虫可以跟踪所有 URL 之前,您的进程正在退出。由于并发性,该main()过程在工作人员完成之前退出。

为了避免这种情况,您可以使用sync.WaitGroup

func Crawl(url string, depth int, fetcher Fetcher, wg *sync.WaitGroup) {
    defer wg.Done()
    if depth <= 0 {
         return
    }
    body, urls, err := fetcher.Fetch(url)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("\nfound: %s %q\n\n", url, body)
    for _,u := range urls {
        if used[u] == false {
           used[u] = true
           wg.Add(1)
           go Crawl(u, depth-1, fetcher, wg)
        }
    }
    return
}

Crawl并调用main如下:

func main() {
    wg := &sync.WaitGroup{}

    Crawl("http://golang.org/", 4, fetcher, wg)

    wg.Wait()
}

另外,不要依赖 map 是线程安全的

于 2012-09-03T15:09:22.453 回答
2

这是一种方法,再次使用sync.WaitGroup但将 fetch 函数包装在匿名 goroutine 中。为了使 url map 线程安全(意味着并行线程不能同时访问和更改值),应该将 url map 包装在一种新类型中,其中包含sync.Mutex类型,即fetchedUrls我示例中的类型,并使用LockandUnlock搜索/更新地图时的方法。

type fetchedUrls struct {
    urls map[string]bool
    mux sync.Mutex
}

// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher, used fetchedUrls, wg *sync.WaitGroup) {
    if depth <= 0 {
        return
    }
    used.mux.Lock()
    if used.urls[url] == false {
        used.urls[url] = true
        wg.Add(1)
        go func() {
            defer wg.Done()
            body, urls, err := fetcher.Fetch(url)
            if err != nil {
                fmt.Println(err)
                return
            }
            fmt.Printf("found: %s %q\n", url, body)
            for _, u := range urls {
                Crawl(u, depth-1, fetcher, used, wg)
            }
            return
        }()
    }
    used.mux.Unlock()
    return
}

func main() {
    wg := &sync.WaitGroup{}
    used := fetchedUrls{urls: make(map[string]bool)}
    Crawl("https://golang.org/", 4, fetcher, used, wg)
    wg.Wait()
}

输出:

found: https://golang.org/ "The Go Programming Language"
not found: https://golang.org/cmd/
found: https://golang.org/pkg/ "Packages"
found: https://golang.org/pkg/os/ "Package os"
found: https://golang.org/pkg/fmt/ "Package fmt"

Program exited.
于 2019-03-23T07:55:37.360 回答