我正在进行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命令,但是程序只找到“http://golang.org/”页面而不是其他页面,而不是递归调用Crawl函数。
当我将go命令添加到函数Crawl的调用中时,为什么程序不起作用?
答案 0 :(得分:7)
问题似乎是,您的流程在所有网址都可以跟踪之前退出
由爬虫。由于并发性,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()
}
答案 1 :(得分:0)
这是一种方法,再次使用sync.WaitGroup,但将获取功能包装在匿名goroutine中。为了使网址映射线程安全(意味着并行线程无法同时访问和更改值),应该将网址映射包装为包含sync.Mutex类型(即fetchedUrls
类型)的新类型在我的示例中,在搜索/更新地图时使用Lock
和Unlock
方法。
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.