练习 Web 爬虫

问题

在这个练习中,我们将会使用 Go 的并发特性来并行化一个 Web 爬虫。

修改 Crawl 函数来并行地抓取 URL,并且保证不重复。

提示:你可以用一个 map 来缓存已经获取的 URL,但是要注意 map 本身并不是并发安全的!

背景知识

  1. 学习 Go语言的sync.Mutex用法,阅读计数器实现的源代码,这个例子也可以参数计数器来实现。
  2. 在多个协程抓取时可以添加互斥锁,这也是Mutex的应用。
  3. 需要你深刻的理解协程的编程方式,也需要注意信道的数据获取方式。

实现思路

  1. 基本代码算是实现了,但是没有做协程安全,如果多个协程同时处理,就会有变量被覆盖的情况。
  2. 还有一种情况,就是并没有对爬虫进行去重判断,有可能实现的爬虫会一直死循环,并需要对map进行互斥锁判断,避免会有冲突。
  3. URL的抓取实现是一些测试用例,并不是真实世界的抓取,现实中的爬虫会比这个更复杂,简单的来讲,最少要添加一个超时的处理,异常的处理。
  4. 在Crawl中如何实现协程抓取URL?如何同步控制map的URL更新?
  5. 定义SafeCounter类型,类似计数器有两个变量,一个是字符串和整数的映射,另外一个是互斥锁的实现。定义一个全局变量sc,在全部的函数里都可以使用。
  6. 由于map不是协程安全,所有验证函数validUrl和标记函数markUrl函数,都需要加下互斥锁。
  7. 并行的抓取,可以在Crawl实现,对抓取回来的内容里的二级URL进行协程处理,并发抓取URL。
    难点在于下面这两个地方:
//方案1
//开启协程处理二级URL
ch := make(chan int, len(urls))
for _, u := range urls {
    go crawl(u, depth-1, fetcher, ch)
}
for i:=0; i < len(urls); i++ {
    //从信道接受数据
     <- ch
}

func crawl(url string, depth int, fetcher Fetcher, ch chan int) {
    Crawl(url, depth, fetcher)
    ch <- 1
}

第一个是,根据对应urls数量开启配置信道缓冲区大小。
通过for循环urls数量,实现并行协程处理,然后重点来了,从信道缓冲区,接受协程发送过来的数据。
第二个,在私有函数crawl,注意,调用完抓取函数Crawl后,需要关闭信道。

细节

一级URL 标题 内容里的二级URL
https://golang.org/ “The Go Programming Language” "https://golang.org/pkg/","https://golang.org/cmd/"
https://golang.org/pkg/ “Packages” "https://golang.org/","https://golang.org/cmd/","https://golang.org/pkg/fmt/","https://golang.org/pkg/os/",
https://golang.org/pkg/fmt/ “Package fmt” "https://golang.org/","https://golang.org/pkg/",
https://golang.org/pkg/os/ “Package os” "https://golang.org/","https://golang.org/pkg/",

实现代码

GitHub

参考链接

  1. 练习:Web 爬虫
  2. GO指南 练习:Web爬虫
  3. 学习一下golang 练习70 web crawler

发表评论

电子邮件地址不会被公开。 必填项已用*标注