Thay vì liên quan đến sync.WaitGroup
, bạn có thể mở rộng kết quả đang được gửi trên url được phân tích cú pháp và bao gồm số lượng URL mới được tìm thấy. Trong vòng lặp chính của bạn, bạn sẽ tiếp tục đọc kết quả miễn là có thứ gì đó để thu thập.
Trong trường hợp số url của bạn được tìm thấy sẽ là số lần xuất hiện thường trình, nhưng không nhất thiết phải là. Cá nhân tôi sẽ sinh ra nhiều hoặc ít cố định các thói quen tìm nạp, vì vậy bạn không mở quá nhiều yêu cầu HTTP (hoặc ít nhất bạn có quyền kiểm soát nó). Vòng lặp chính của bạn sẽ không thay đổi sau đó, vì nó không quan tâm cách tìm nạp đang được thực thi. Thực tế quan trọng ở đây là bạn cần gửi kết quả hoặc lỗi cho mỗi url - Tôi đã sửa đổi mã ở đây, vì vậy nó không sinh ra các thói quen mới khi độ sâu đã là 1.
Tác dụng phụ của giải pháp này là bạn có thể dễ dàng in tiến trình trong vòng lặp chính của bạn.
Dưới đây là ví dụ về sân chơi:
http://play.golang.org/p/BRlUc6bojf
package main
import (
"fmt"
)
type Fetcher interface {
// Fetch returns the body of URL and
// a slice of URLs found on that page.
Fetch(url string) (body string, urls []string, err error)
}
type Res struct {
url string
body string
found int // Number of new urls found
}
// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher, ch chan Res, errs chan error, visited map[string]bool) {
body, urls, err := fetcher.Fetch(url)
visited[url] = true
if err != nil {
errs <- err
return
}
newUrls := 0
if depth > 1 {
for _, u := range urls {
if !visited[u] {
newUrls++
go Crawl(u, depth-1, fetcher, ch, errs, visited)
}
}
}
// Send the result along with number of urls to be fetched
ch <- Res{url, body, newUrls}
return
}
func main() {
ch := make(chan Res)
errs := make(chan error)
visited := map[string]bool{}
go Crawl("http://golang.org/", 4, fetcher, ch, errs, visited)
tocollect := 1
for n := 0; n < tocollect; n++ {
select {
case s := <-ch:
fmt.Printf("found: %s %q\n", s.url, s.body)
tocollect += s.found
case e := <-errs:
fmt.Println(e)
}
}
}
// fakeFetcher is Fetcher that returns canned results.
type fakeFetcher map[string]*fakeResult
type fakeResult struct {
body string
urls []string
}
func (f fakeFetcher) Fetch(url string) (string, []string, error) {
if res, ok := f[url]; ok {
return res.body, res.urls, nil
}
return "", nil, fmt.Errorf("not found: %s", url)
}
// fetcher is a populated fakeFetcher.
var fetcher = fakeFetcher{
"http://golang.org/": &fakeResult{
"The Go Programming Language",
[]string{
"http://golang.org/pkg/",
"http://golang.org/cmd/",
},
},
"http://golang.org/pkg/": &fakeResult{
"Packages",
[]string{
"http://golang.org/",
"http://golang.org/cmd/",
"http://golang.org/pkg/fmt/",
"http://golang.org/pkg/os/",
},
},
"http://golang.org/pkg/fmt/": &fakeResult{
"Package fmt",
[]string{
"http://golang.org/",
"http://golang.org/pkg/",
},
},
"http://golang.org/pkg/os/": &fakeResult{
"Package os",
[]string{
"http://golang.org/",
"http://golang.org/pkg/",
},
},
}
Và vâng, hãy làm theo @jimt tư vấn và thực hiện truy cập vào các chủ đề an toàn bản đồ.
Tôi đã đánh dấu đây là câu trả lời, bởi vì trong khi tôi nghĩ rằng giải pháp @jimt cung cấp là thanh lịch và dạy rất nhiều công cụ hữu ích, điều này được thực hiện mà không dựa vào các công cụ vượt ra ngoài phạm vi của các hướng dẫn trước. Tôi đã yêu cầu 'câu trả lời chính xác' thành ngữ, tuy nhiên, và khi tôi không đủ kinh nghiệm, tôi không biết đó là cái tôi muốn đánh dấu cho điều đó. (bị cám dỗ chấp nhận cả hai câu trả lời) – SamMorrowDrums