golang初学之goroutine---web爬虫

smallfrog 发布于1年前 阅读10579次
0 条评论

go tour 练习 https://tour.go-zh.org/concurrency/10

package main

import (
	"fmt"
	"sync"
	"time"
)

type Fetcher interface {
	// Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。
	Fetch(url string) (body string, urls []string, err error)
}

// SafeCounter 的并发使用是安全的。
type SafeUrlMap struct {
	v   map[string]bool
	mux sync.Mutex
}

func (c *SafeUrlMap) Put(key string) {
	c.mux.Lock()
	c.v[key] = true
	c.mux.Unlock()
}

func (c *SafeUrlMap) Contains(key string) bool {
	c.mux.Lock()
	defer c.mux.Unlock()
	_, ok := c.v[key]
	return ok
}

type Resp struct {
	url string
	body string
}

var urlMap *SafeUrlMap = &SafeUrlMap{v: make(map[string]bool)}
// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。
func Crawl(url string, depth int, fetcher Fetcher, ch chan Resp) {
	if depth <= 0 {
		return
	}
	urlMap.Put(url)
	body, urls, err := fetcher.Fetch(url)
	if err != nil {
		fmt.Println(err)
		return
	}
	ch <- Resp{url:url, body:body}

	for _, u := range urls {
		if urlMap.Contains(u) {
			fmt.Printf("Have Processed: %s\n", u)
			continue
		}
		go Crawl(u, depth-1, fetcher, ch)
	}
	
	return
}

func main() {
	ch := make(chan Resp)
	go Crawl("http://golang.org/", 4, fetcher, ch)
	boom := time.After(3 * time.Second)
	for {
		select {
			case r := <-ch:
				fmt.Printf("found: %s %q\n", r.url, r.body)
				boom = time.After(3 * time.Second)
			case <-boom:
				fmt.Printf("time out\n")
				return
			}
	}
		
}

// fakeFetcher 是返回若干结果的 Fetcher。
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 是填充后的 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/",
		},
	},
}

  

需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。