在Go语言中实现一个并发爬虫,可以使用标准库中的net/http
包来发送HTTP请求,使用golang.org/x/net/html
包来解析HTML,以及使用sync
包中的WaitGroup
来同步并发的goroutines。下面是一个简单的并发爬虫示例,它会并发地访问网页,并打印出网页中的所有链接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
package main
import (
"fmt"
"net/http"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
"sync"
)
// 用于存储访问的URL
var urls = []string{
"https://plumephp.com/posts/",
"https://plumephp.com/about/",
// 可以添加更多的URL
}
// 用于同步goroutines
var wg sync.WaitGroup
func main() {
// 启动所有goroutines
for _, url := range urls {
wg.Add(1)
go crawl(url)
}
// 等待所有goroutine完成
wg.Wait()
}
// crawl 函数用于访问URL并打印出网页中的所有链接
func crawl(url string) {
defer wg.Done()
// 发送HTTP GET请求
resp, err := http.Get(url)
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
// 解析HTML
doc, err := html.Parse(resp.Body)
if err != nil {
fmt.Println(err)
return
}
// 遍历DOM树,查找所有的a标签
forEachNode(doc, atom.A, func(n *html.Node) {
// 获取href属性
for _, a := range n.Attr {
if a.Key == "href" {
fmt.Println(a.Val)
}
}
})
}
// forEachNode 函数遍历DOM树,查找指定的标签
func forEachNode(n *html.Node, tag atom.Atom, f func(*html.Node)) {
if n.Type == html.ElementNode && n.DataAtom == tag {
f(n)
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
forEachNode(c, tag, f)
}
}
|
代码解释:
- 我们定义了一个
urls
切片,里面包含了需要爬取的网页URL。
- 使用
sync.WaitGroup
来同步并发的goroutines。
main
函数中,我们遍历urls
切片,为每个URL启动一个goroutine,并使用crawl
函数来处理。
crawl
函数首先使用http.Get
发送HTTP GET请求,然后使用html.Parse
解析HTML。
- 使用
forEachNode
函数遍历DOM树,查找所有的<a>
标签,并打印出它们的href
属性值。
forEachNode
是一个递归函数,它查找指定的标签并执行提供的回调函数。
请注意,这个示例是一个基础的并发爬虫,实际使用中可能需要考虑更多的因素,比如错误处理、重试机制、用户代理设置、robots.txt协议遵守等。此外,对于大规模的爬虫项目,可能需要使用更高级的并发控制和调度机制。