Gocolly爬虫

gocolly 爬虫

1
go get -u github.com/gocolly/colly/v2/...
colly框架依赖goquery库,goquery将jQuery的语法和特性引入到了go语言中。如果要灵活自如地采集数据,首先要了解jQuery选择器
参考别人整理的信息比较全

https://www.cnblogs.com/majianguo/p/8146130.html

官方例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
c := colly.NewCollector()

// Find and visit all links
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
e.Request.Visit(e.Attr("href"))
})

c.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting", r.URL)
})

c.Visit("http://go-colly.org/")
}

写个Demo,采集七猫小说,查找页面书库的链接,只要获取到存入redis,验证redis是否存储过url,然后无限循环获取书库链接里面的标题等数据。。。

控制器 https://github.com/itwangmingfei/Gin-go-demo/blob/master/controllers/gocoll.go
Redis https://github.com/itwangmingfei/Gin-go-demo/blob/master/tools/redis.go
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
@爬取七猫小说网
*/
func (g Gocoll) get(c *gin.Context){
dlink :="https://www.qimao.com"
cfg := config.GetConfig()
var novel models.Novel
var clientredis tools.GoRedis
//*********************************

coll := colly.NewCollector(
colly.AllowedDomains("www.qimao.com"),
colly.Async(true),
colly.UserAgent("Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"),
)

///这个地方是配置redis存储请求返回的数据信息吧?

storage := &redisstorage.Storage{
Address: fmt.Sprintf("%s:%s", cfg.Redis.Host,cfg.Redis.Port),
Password:cfg.Redis.Passwd,
DB: cfg.Redis.Db,
Prefix : "HTTP_QIMAO",
}
if err := coll.SetStorage(storage);err!=nil{
panic(err)
}
if err:=storage.Clear();err!=nil{
log.Fatal(err)
}
//defer storage.Client.Close()
//*----------------------------------------------

q,_ :=queue.New(2,storage)
/*<h2 class="tit">????</h2>*/
/*获取样式 div.data-txt 模块下的数据信息*/
coll.OnHTML(`div.data-txt`, func(e *colly.HTMLElement) {
ls,_ := e.DOM.Html()

//标题
tit := e.ChildText("h2.tit")
//作者
pname := e.ChildText(`p.p-name a`)
//状态
status := e.ChildText(`span.qm-tags.black.clearfix em:first-child`)

nums := e.ChildText(`p.p-num span:nth-child(1)`)
nums1 := e.ChildText(`p.p-num span:nth-child(3)`)
nums2 := e.ChildText(`p.p-num span:nth-child(5)`)

//没有标签正则一下
reg := regexp.MustCompile(`<em>主角:</em>(.*?)<`)
regstr := reg.FindAllStringSubmatch(ls,-1)

uptime := e.ChildText(`p.p-update em.time`)

newpage :=e.ChildText(`p.p-update a`)


if len(tit)>0 {
novel.Title = tit
fmt.Printf("名称:%s \n",tit)
}
if len(regstr) >0 {
novel.Mster = regstr[0][1]
fmt.Printf("主角:%s \n",regstr[0][1])
}
if len(pname) >0 {
fmt.Printf("作者:%s \n",pname)
novel.Author = pname
}
if len(status) >0 {
fmt.Printf("状态:%s \n",status)
novel.Status = status
}
if len(nums)>0{
fmt.Printf("数量:%s %s %s \n",nums,nums1,nums2)
novel.Show = nums+nums1+nums2
}
if len(uptime)>0{
fmt.Printf("更新时间:%s \n",uptime)
novel.Uptime =uptime

}
if len(newpage)>0{
fmt.Printf("最新章节:%s \n",newpage)
novel.Newpage = newpage
}
models.GetDb().Create(&novel)
})

//获取url
coll.OnHTML(`a[href]`,func(e *colly.HTMLElement){
link := e.Attr("href")
//正则匹配书库
reg := regexp.MustCompile(`^(/shuku/[1-9]{6}/)`)
res := reg.FindAllString(link,-1)
//存在返回数据
if len(res)>0{
//存入redis中
newLink := dlink +link
//判断执行存储的redis中是否存在如果存在不存储当前url
Isset := clientredis.ToIsset(newLink)

//如果不存在继续抓取当前链接
if !Isset{
//连接存入redis中
log.Println(newLink)
err := clientredis.DoLpush(newLink)
if err!=nil{
log.Println(err)
}
}

}
})

coll.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting", r.URL.String())
})
coll.OnScraped(func(r *colly.Response) {
fmt.Println("Finished", r.Request.URL)
})
//获取这个应该是配饰redisstorage使用 用于接收返回数据然后存储
coll.OnResponse(func(r *colly.Response) {
log.Println(coll.Cookies(r.Request.URL.String()))
})

novel.Url = dlink
q.AddURL(dlink)
q.Run(coll)
for {
/*
@获取新的链接
*/
dlink,_ := clientredis.DoRpop()
if len(dlink)!=0{
novel.Url = dlink
q.AddURL(dlink)
q.Run(coll)
time.Sleep(time.Second*2)
}

}

}

Redis 定义

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
71
72
73
package tools

import (
"fmt"
"gin/config"
"github.com/monnand/goredis"
log "github.com/sirupsen/logrus"
)

type GoRedis struct {
*goredis.Client
}

var Client GoRedis

const (
DO_QUEUE = "DoQueue" //记录需要执行的数据
TO_QUEUE = "Toqueue" //处理过的数据放这里
)

//链接redis
func InitRedis(cfg config.Redis) {
var cliect goredis.Client
cliect.Addr = fmt.Sprintf("%s:%s", cfg.Host, cfg.Port)
cliect.Password = cfg.Passwd
cliect.Db = cfg.Db
pong, err := cliect.Ping()
if err != nil {
log.Fatalf("cant ping redis %s\n", err.Error())
}
Client = GoRedis{&cliect}
log.Println("ping redis:", pong)
}

//存入数据头部存入数据
func (r GoRedis) DoLpush(value string) error {
err := Client.Lpush(DO_QUEUE, []byte(value))
if err != nil {
log.Println(err)
return err
}
return nil
}

//读取数据尾部获取数据
func (r GoRedis) DoRpop() (string, error) {
val, err := Client.Rpop(DO_QUEUE)
if err != nil {
return "", err
}
return string(val), err
}

//获取当前key存入的数量
func (r GoRedis) DoLen() int {
nums, err := Client.Llen(DO_QUEUE)
if err != nil {
return 0
}
return nums
}

//存入集合中------------------------------------
func (r GoRedis) ToSadd(value string) bool {
res, _ := Client.Sadd(TO_QUEUE, []byte(value))
return res
}

//获取当前value是否需要存
func (r GoRedis) ToIsset(value string) bool {
res, _ := Client.Sismember(TO_QUEUE, []byte(value))
return res
}