[Golang]实习最后一天小纪念+并发爬虫小练习

  今天是我在公司实习的最后一天,一个月的时间真的是太短暂了,我非常享受在公司工作的这一个月,在这里Leader和同事们对我的帮助极大地促进了我技术水平的进步和自信心的提升,我发自内心地感谢白山云科技给我这样一个实习的机会,谢谢你们对我的无私帮助(虽然他们可能也看不到…)。

  眼看着暑假还剩不到一个月,其实我内心是不想离开这个公司的,但是想到开学以后又要有各种网络赛和区域赛,又有新生的到来,为了校队,我还是离开吧(虽然我回去也没啥卵用…)。

  实习期间第一次写函数式的程序,并且第一次完成了一个用于日志分析的分布式程序。还有腾讯的实时监控数据上报的需求的边缘服务器日志收集和汇总的功能,第一次为自己的代码写了测试。第一次与他人真正地合作完成一个项目,虽然到今天下午的时候leader说腾讯给出了新接口,我们分析了一下需求表示要改一大部分东西,不过我是没有机会再做下去了,留给zzb同学吧,估计他看了我的代码要推翻重写了,哈哈。

  记得刚来的时候我一直担心leader分配给我任务,我做不好。但是心一横接下来,不管做得好不好,只要用心去做,哪怕结果不尽人意,还是会得到宽容和认可(好吧我现在觉得我写的东西都是一坨shi…)。

  人总归有第一次,在随后的磕磕绊绊中学习,直到做出一点微小的贡献。慢慢地,力所能及的地方越来越高,直到自己羽翼丰满,独当一面,这就是奋斗和成长最吸引人的地方了吧。

  不知不觉我就要大三了,大学生活已经度过了一半。大一大二的时候总觉得一些具体的技术我没必要深究,于是就全部搁浅。经过这次实习我的心态有了变化,对自己未来的规划也应当再做一些调整了:

多读一些书,在内存管理、并行代码编写上多下一些功夫。算法再优雅但终归是跑在单核上的,并行程序才适合这个时代。

复习一下网络和数据库的内容。

多看一些语言的第三方库,自己造轮子会浪费很多时间。

多尝试写自己没有写过的东西,不要担心自己第一次写不出来,谁都有第一次,第一次也往往不是顺利的。

复习好数学,继续学习机器学习的知识。

刷题是必须持续进行的,帮助我提高思维水平,以便更好地接受其他知识。

系统地学一下Linux,并且和windows说再见。

  现在就想起来这么多,等到以后继续补充吧。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

  爬虫的需求很简单,就是从paste.ubuntu.com上搞别人贴的代码,然后判断是不是C/C++代码。面临着几个问题需要考虑:

1、paste访问的时间很长,这很影响爬虫和内容处理的效率,这该怎么办:

  这个问题我使用了三种方法来解决,第一个是采用并行程序,这个就不多说了,多开几个线程做处理会快些。第二个是预判这个文件的大小,设定一个阈值,保证这个文件超过阈值后一定不是C/C++的题解代码,当然塞一张表除外,不过OJ一般限制65535B,我们可以取一半即30000B。第三个是设置timeout,在golang里就需要手工建立一个client发送get请求了,这里的具体函数如下:

func GetHtml(Url string, MagicNum string) []byte {
	timeout := time.Duration(15 * time.Second)
	client := http.Client{
		Timeout: timeout,
	}
	request, err := http.NewRequest(http.MethodGet, Url, nil)
	
	if err != nil {
		log.Errorln(err.Error())
		return nil
	}
	res, err := client.Do(request)
	if err != nil {
		log.Errorln(err.Error())
		return nil
	}
	result, err := ioutil.ReadAll(res.Body)
	if len(result) > 30000 {
		return nil
	}
	res.Body.Close()
	if err != nil {
		log.Errorln(err.Error())
		return nil
	}
	rd := bytes.NewReader(result)
	nd, err := goquery.Parse(rd)
	if err != nil {
		log.Panicln(err.Error())
	}
	str := nd.Find("td.code").Text()

	buf := []byte(str)
	return buf
}
GetHtml

2、判断这个html中包含的代码是C/C++代码:

  这个我处理得很糙,就是去看看有没有一个#include子串,当然这个子串是可以增加的,不过可能影响效率。希望有更好办法的朋友可以留下你的方法,在此谢谢。

func Judge(buf []byte) bool {
	var patterns = []string{
		"#include",
	}
	n := len(patterns)
	for i := 0; i < n; i++ {
		ok, err := regexp.Match(patterns[i], buf)
		if ok {
			return true
		}
		if err != nil {
			log.Errorln(err.Error())
			return false
		}
	}
	return false
}
Judge

3、并行访问时如何保证访问不冲突:

  这个也很好解决,学过操作系统的人都明白可以添加一个mutex来保证一个资源不被同时访问。我这里更新的是一个offset,道理是一样的。这个os.Args[1]和offset都是全局变量,但是只有offset需要写,所以只给它加mutex就行了。

func numGetter(MagicNum string, ch chan int64) {
	Num, err := strconv.ParseInt(MagicNum, 10, 64)
	if err != nil {
		log.Errorln(err.Error())
		return
	}
	ch <- (Num + Offset)
	Offset++
}
numGetter
func NumGetter(Num string, ch chan int64) {
	for {
		mutex.Lock()
		numGetter(Num, ch)
		mutex.Unlock()
		Do(strconv.FormatInt(<-ch, 10))
	}
}
NumGetter

  最后并行程序,一个go就可以解决~os.Args[2]用于设定线程数。

func main() {
	Offset = 0
	ch := make(chan int64, 10)
	n, err := strconv.ParseInt(os.Args[2], 10, 64)
	if err != nil {
		log.Errorln(err.Error())
		return
	}
	var i int64
	for i = 0; i < n; i++ {
		go NumGetter(os.Args[1], ch)
	}
	time.Sleep(time.Hour)
}
main

  这是我第一次尝试用golang写并行的爬虫程序。我从学习写golang到现在有半个月的时间了,是时候找一本书认真学习一下go这个美妙的语言了。现在写的程序都是在翻译自己脑子里用其他语言写好的程序而已,我应该更多地去使用go语言的特性才可以。

  ps: 使用了两个go的第三方库:

"github.com/Sirupsen/logrus"
"github.com/opesun/goquery"

附图:


原文地址:https://www.cnblogs.com/kirai/p/5743190.html