在这个章节中,我们将会结合之前的知识点,实现一个简易的并发网页爬虫。我们的爬虫会先爬取一个起始页面,提取出所有的链接,然后并发地爬取这些链接。
使用 goquery 提取链接
首先,我们需要写一个函数来抓取网页并提取出所有的链接。这里我们使用 goquery
来解析 HTML 并提取链接。安装 goquery
可以使用 go get
命令:
go get github.com/PuerkitoBio/goquery
然后我们来实现 fetchAndParse
函数:
func fetchAndParse(url string) ([]string, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, err
}
var links []string
doc.Find("a").Each(func(i int, s *goquery.Selection) {
href, exists := s.Attr("href")
if exists {
links = append(links, href)
}
})
return links, nil
}
并发爬取链接
接下来,我们需要写一个函数来并发地爬取链接。我们将使用 goroutine 和 channel 来实现并发。为了避免同时打开过多的 HTTP 连接,我们将同时并发的 goroutine 数量限制在一定范围内,这可以通过使用带缓冲的 channel 来实现。
func crawlLinks(startURL string) {
// 创建一个带缓冲的 channel,限制同时并发的 goroutine 数量
worklist := make(chan []string, 20)
go func() { worklist <- []string{startURL} }()
seen := make(map[string]bool)
// 开始并发爬取
for list := range worklist {
for _, link := range list {
if !seen[link] {
seen[link] = true
go func(link string) {
links, err := fetchAndParse(link)
if err == nil {
worklist <- links
}
}(link)
}
}
}
}
整合到一起
最后,我们将这些函数整合到一起,创建我们的并发网页爬虫:
func main() {
startURL := "http://example.com"
crawlLinks(startURL)
}
现在,你已经有了一个简单的并发网页爬虫。虽然这个爬虫还很简单,但它已经能够展示 Go 的并发特性。
这个爬虫还有很多可以改进的地方。例如,你可以添加错误处理,遵守 robots.txt,处理重定向,添加延时以避免对服务器造成过大压力等。这些改进都可以作为你继续学习 Go 和并发编程的练习。
希望这个章节对你有所帮助,如果你有任何疑问,欢迎随时提问。
推荐阅读:
https://mp.weixin.qq.com/s/dV2JzXfgjDdCmWRmE0glDA
https://mp.weixin.qq.com/s/an83QZOWXHqll3SGPYTL5g
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。