投稿日
2012/4/3 火曜日
A Tour of Goの69 Exercise: Web Crawlerをやってみました。
68をやった直後から色々考えたり試してみたりしてたんですが、うまく行かず、丸1日程度間を開けてから最初から考えなおしてみたらあっさりいきました。
効果を実感しやすくするため、Fetchでtime.Sleepするようにしてますが、オンラインのA Tour of Goではtime.Sleepは使えないみたいなので、ローカルのもので試してます。
というか、オンラインのA Tour of Goは制限があるし、ローカルのA Tour of Goは無限ループに陥ったりしたときにやり直しが面倒だしで、普通にテキストエディタでソース書いて、コマンドラインでコンパイル・実行したほうが、ちょっと変更しては試しを繰り返すときは便利です。
こういう書き方でいいんですかね。
サンプルデータがしょぼいからいいですけど、実際は同時に実行するスレッド数は制限したほうがいいはず。
Next構造体はCrawlCommandみたいな名前にすべきだった。
chan CrawlResultじゃなくてchan *CrawlResultなチャンネルを使うと、結果の有無をnilとの比較で判定できるし、効率上も有利かもしれない。
package main
import (
"fmt"
"time"
)
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)
}
// 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 CrawlResult) {
// This implementation doesn't do either:
if depth <= 0 {
ch <- CrawlResult{}
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
ch <- CrawlResult{Err: err}
return
}
fmt.Printf("found: %s %q\n", url, body)
ch <- CrawlResult{URLs: urls, Crawled: url, Depth: depth}
return
}
type Next struct {
URL string
Depth int
}
type CrawlResult struct {
URLs []string
Crawled string
Depth int
Err error
}
func main() {
ch := make(chan CrawlResult)
nexts := []Next{Next{"http://golang.org/", 4}}
crawleds := make(map[string]int)
for len(nexts) > 0 {
goroutines := 0
for _, n := range nexts {
if crawleds[n.URL] > 0 {continue}
go Crawl(n.URL, n.Depth, fetcher, ch)
goroutines++
fmt.Printf("goroutine %d start\n", goroutines)
}
nexts = make([]Next, 0)
for i := 0; i < goroutines; i++ {
ret := <-ch
if ret.Err != nil {fmt.Println(ret.Err)}
if ret.Crawled == "" {continue}
crawleds[ret.Crawled]++
for _, url := range ret.URLs {
nexts = append(nexts, Next{url, ret.Depth-1})
}
}
}
}
// 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) {
time.Sleep(1000 * time.Millisecond)
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/",
},
},
}
最近のコメント
たかたむ
はじめまして。初リアルフォース(R3ですが)で,同…
nokiyameego
ZFS poolのデバイスラベル破損で悩んていたと…
名前
しゅごい
Jane Doe
FYI Avoid Annoying Unexpe…
Jane Doe
ご存じとは思いますが、whileには、”~の間”と…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…