GO笔记:解决爬虫限制http并发数量的问题

原创
2017/06/09 10:56
阅读数 1.2K

假设目标网站结构是:列表页 + 内容页

列表页url是规律的,如 http://DOMAIN/list/page/1

计划采集 1-4000 页,每页10条内容,一共40000个内容页。

假设按照深度优先的采集策略,即按照每抓取一个列表页,提取内容页url,紧接着抓取内容页并提取内容,然后入库。

首先第一步,采集入口,我们使用for循环,为每个列表页创建一个线程去采集。

for n:=1;n<=4000;n++ {
    go CrawlList(fmt.Sprintf("http://DOMAIN/list/page/%d"), n)
}

CrawlList 来抓取列表页,并创建内容页的抓取请求

func CrawlList(listurl string) {
    // http抓取列表页源码, 假设这里封装了个函数, 内部也是异步的,返回一个channel用于接收结果
    ch := HtmlGet(listurl)
    html <- ch
    //提取内容页url,可以使用正则或者 goquery等包 过程省略...
    go CrawlContent(contentUrl)    
}

func CrawlContent(contentUrl string) {
    //抓取内容页源码
    ch := HtmlGet(contentUrl)
    html <- ch
    //提取内容页内容,并入库,过程略    
}

大致过程就是这样,现在问题来了,对方站点限制了访问频率,如果并发过高,会被对方防火墙拦截。所以我们首先要对 HtmlGet 增加并发限制,代码大致原理如下

//创建一个带缓冲的channel,容量为10 , 支持10个并发
var conlimit = make(chan bool, 10) 
func HtmlGet(addr string) chan string {
    conlimit <- true
    ch := make(chan string)
    go func(){
        defer func(){ <-conlimit }()
        //http请求过程略,假设内容保存到 res ,最终通过ch提供消费
        ch <- res    
    }()
    return ch
}

这样,我们可以针对http请求限制同时最多10个并发。

然而还有一个问题,因为我们是深度采集的,所以,一开始我们创建了4000个goroutine采集列表页,那么每个goroutine中都会调用 GetHtml 函数去抓取列表页内容,等于必须等到所有列表页几乎都请求过,才会轮到内容页的采集,在这之前,所有已经请求成功的列表页都会占着内存,等待内容页采集成功。

所以,在第一步我们也需要限制一下,每次最多采集10个列表页,这样10个列表页抓取成功了,会立即处理下面的内容页,也就是最多 10*10个goroutine,不会占用太多内存。

listconlimit := make(chan bool, 10) 
for n:=1;n<=4000;n++ {
    listconlimit <- true
    go func(n int){
        defer func(){ <- listconlimit }()
        CrawlList(fmt.Sprintf("http://DOMAIN/list/page/%d"), n)    
    }(n)
}

注: 以上代码仅视为伪代码,不能直接运行。

-完-

展开阅读全文
Go
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部