A Tour of Go 70 Exercise: Web Crawler
演習: ウェブクローラ( web crawler )を並列化する。
なにはともあれ、あ、つぅぁー、おぶ、ごぉー完走w=
package main import ( "errors" "fmt" //dbgfmt "fmt" "sync" ) 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) } var fetched = struct { m map[string]error sync.Mutex }{m : make(map[string]error)} var loading = errors.New("url load in progress") func crawlImpl(url string, depth int, fetcher Fetcher) { if depth <= 0 { return } fetched.Lock() if _, ok := fetched.m[url]; ok { fetched.Unlock() fmt.Println("<- Done with %v, already fetched.", url) return } fetched.m[url] = loading fetched.Unlock() /* s := make([]string, 4-depth) dbgfmt.Println(s,body,url,depth) dbgfmt.Println("@",body,urls,err) */ body, urls, err := fetcher.Fetch(url) fetched.Lock() fetched.m[url] = err fetched.Unlock() if err != nil { fmt.Println("<- Error on %v: %v",url,err) return } fmt.Printf("Found: %s %q\n", url, body) done := make(chan bool) for i, u := range urls { fmt.Println("-> Crowling child %v/%v of %v : %v.",i, len(urls), url, u) go func(url string){ crawlImpl(u, depth-1, fetcher) done <- true }(u) } for i := range urls { fmt.Println("<- [%v] %v/%v Waiting for child %v.", url, i, len(urls)) <-done } fmt.Println("<- Done with %v", url) } // Crawl uses fetcher to recursively crawl // pages starting with url, to a maximum of depth. func Crawl(url string, depth int, fetcher Fetcher) { // TODO: Fetch URLs in parallel. // TODO: Don't fetch the same URL twice. // This implementation doesn't do either: crawlImpl(url,depth,fetcher) for url,err := range fetched.m { if err != nil { fmt.Println("%v failed: %v", url, err) } else { fmt.Println("%v was fetched", url) } } return } func main() { Crawl("http://golang.org/", 4, fetcher) } // 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/", }, }, }