1. 代理池
github找一个proxy pool轮子,后面爬虫要用
2. http库的选用
我之前一直使用go-colly框架进行爬虫,后来发现了未知的严重的内存泄漏bug,改换自带的net/http库就没有内存泄漏的问题。而且colly能实现的爬虫,net/http库实现起来也毫无压力。所以这里推荐使用net/http库,高并发NIO且占用内存小。
net/http常用几点:代理池,get,post raw和json,header设置,cookie设置,session,user agent推荐go-colly自带的生成器,超时设置,失败重试次数(得上层逻辑实现)。
一个使用示例:
proxyFunc := func(*http.Request) (*url.URL, error) {
resp, err := http.Get(proxyURL)
if err != nil {
return nil, err
}
ip, _ := ioutil.ReadAll(resp.Body)
_ = resp.Body.Close()
return url.Parse("http://" + string(ip))
}
client := &http.Client{Timeout: time.Second * 5}
client.Transport = &http.Transport{
Proxy: proxyFunc,
}
// ……
resp, err := client.Post(crawlURL, "application/json", strings.NewReader(requestData))
if err != nil {
continue
}
body, _ := ioutil.ReadAll(resp.Body)
_ = resp.Body.Close()
// ……
3. 协程池的实现
go虽然原生支持高并发,但无脑开协程会让内存一下子吃满,所以并发爬虫一定要写协程池,我最常用有两种写法,也是go官网推荐的两种写法。
第一种,起固定数目个协程:
poolSize := 128
recv := make(chan string, poolSize)
wg := sync.WaitGroup{}
for i := 0; i < maxGoRoutine; i++ {
wg.Add(1)
go func() {
for x := range recv {
time.Sleep(time.Millisecond * 20)
// ……
}
wg.Done()
}()
}
for _, x := range xxx {
recv <- x
}
close(recv)
wg.Wait()
第二种,semaphore:
poolSize := 128
sem := make(chan struct{}, poolSize)
defer close(sem)
wg := sync.WaitGroup{}
for _, x := range xxx {
sem <- struct{}{}
wg.Add(1)
x := x // 这里也可以直接传参给go func
go func() {
defer func() {
<-sem
wg.Done()
}()
// ……
}()
}
wg.Wait()
4. 爬取内容存储
由于爬取的东西基本用于本地分析,这里推荐一些嵌入式单机数据库。
- go语言实现的leveldb,无需任何依赖(只需导入一个go包)的高性能单机kv存储引擎
- boltdb,同样是基于go语言实现的kv存储引擎,区别于leveldb,leveldb基于LSM树,boltdb基于B+树。
- sqlite3,众所周知的单机关系型数据库。
5. 关于爬虫的一些技巧
- 用浏览器抓包,用fiddler/charles抓包,用js fetch/postman模拟请求。
- 浏览器查看元素,copy path,查看network,copy request。
- 浏览器断点调试js,分析request是如何构造的以及response是如何解析的。