题目:
练习:Web 爬虫
在这个练习中,我们将会使用 Go 的并发特性来并行化一个 Web 爬虫。
修改 Crawl
函数来并行地抓取 URL,并且保证不重复。
提示:你可以用一个 map 来缓存已经获取的 URL,但是要注意 map 本身并不是并发安全的!
思路:
借鉴利用队列实现广度优先算法的思路,把第一个url当做根节点,根节点下面的url当做孩子节点,队列当做管道。
- 把第一个url作为根节点,查询[showCrawl()函数]
- 将对应孩子节点集合加入管道。
- 遍历这个孩子节点集合
- 重复2~3步骤
- 直到管道中没有元素
实现:
- (完整代码见最后面)
- 一些关键性问题在本代码后面
// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。
func Crawl(url string, depth int, fetcher Fetcher) {
// 初始化工作
if depth < 1 {
return
}
Urls := make(chan []string, 10) //相当于队列
go showCrawl(url, Urls, fetcher) // 根节点遍历
flag := 1 // 用于探测管道中还有多少个元素
// 2. 根据要求的深度(depth)查询 : 向下查询多少层
for i := depth; i > 0; i-- {
// 只要管道里有元素,就读取
if flag > 0 {
urls := <- Urls // urls == []string
flag-- // 管道元素数量--
for _, url := range urls {
// 一个页面下的url集合
fmt.Printf("接收到的url:%s\n", url)
if _, ok := store[url]; !ok {
// 判断是否查询过
go showCrawl(url, Urls, fetcher)
flag++
}
}
}
}
查询函数:
// 查询页面,并将页面下的url放到管道中