golang并发爬虫
爬虫的需求很简单,就是从paste.ubuntu.com上搞别人贴的代码,然后判断是不是C/C++代码。面临着几个问题需要考虑:
1、paste访问的时间很长,这很影响爬虫和内容处理的效率,这该怎么办:
这个问题我使用了三种方法来解决,第一个是采用并行程序,这个就不多说了,多开几个线程做处理会快些。第二个是预判这个文件的大小,设定一个阈值,保证这个文件超过阈值后一定不是C/C++的题解代码,当然塞一张表除外,不过OJ一般限制65535B,我们可以取一半即30000B。第三个是设置timeout,在golang里就需要手工建立一个client发送get请求了,这里的具体函数如下:
funcGetHtml(Urlstring,MagicNumstring)[]byte{
timeout:=time.Duration(15*time.Second)
client:=http.Client{
Timeout:timeout,
}
request,err:=http.NewRequest(http.MethodGet,Url,nil)
iferr!=nil{
log.Errorln(err.Error())
returnnil
}
res,err:=client.Do(request)
iferr!=nil{
log.Errorln(err.Error())
returnnil
}
result,err:=ioutil.ReadAll(res.Body)
iflen(result)>30000{
returnnil
}
res.Body.Close()
iferr!=nil{
log.Errorln(err.Error())
returnnil
}
rd:=bytes.NewReader(result)
nd,err:=goquery.Parse(rd)
iferr!=nil{
log.Panicln(err.Error())
}
str:=nd.Find("td.code").Text()
buf:=[]byte(str)
returnbuf
}
GetHtml
2、判断这个html中包含的代码是C/C++代码:
这个我处理得很糙,就是去看看有没有一个#include子串,当然这个子串是可以增加的,不过可能影响效率。希望有更好办法的朋友可以留下你的方法,在此谢谢。
funcJudge(buf[]byte)bool{
varpatterns=[]string{
"#include",
}
n:=len(patterns)
fori:=0;i<n;i++{
ok,err:=regexp.Match(patterns[i],buf)
ifok{
returntrue
}
iferr!=nil{
log.Errorln(err.Error())
returnfalse
}
}
returnfalse
}
Judge
3、并行访问时如何保证访问不冲突:
这个也很好解决,学过操作系统的人都明白可以添加一个mutex来保证一个资源不被同时访问。我这里更新的是一个offset,道理是一样的。这个os.Args[1]和offset都是全局变量,但是只有offset需要写,所以只给它加mutex就行了。
funcnumGetter(MagicNumstring,chchanint64){
Num,err:=strconv.ParseInt(MagicNum,10,64)
iferr!=nil{
log.Errorln(err.Error())
return
}
ch<-(Num+Offset)
Offset++
}
numGetter
funcNumGetter(Numstring,chchanint64){
for{
mutex.Lock()
numGetter(Num,ch)
mutex.Unlock()
Do(strconv.FormatInt(<-ch,10))
}
}
最后并行程序,一个go就可以解决~os.Args[2]用于设定线程数。
funcmain(){
Offset=0
ch:=make(chanint64,10)
n,err:=strconv.ParseInt(os.Args[2],10,64)
iferr!=nil{
log.Errorln(err.Error())
return
}
variint64
fori=0;i<n;i++{
goNumGetter(os.Args[1],ch)
}
time.Sleep(time.Hour)
}
这是我第一次尝试用golang写并行的爬虫程序。我从学习写golang到现在有半个月的时间了,是时候找一本书认真学习一下go这个美妙的语言了。现在写的程序都是在翻译自己脑子里用其他语言写好的程序而已,我应该更多地去使用go语言的特性才可以。
原文链接: