分布式爬虫小记

前言

距离上一篇博客不知不觉居然过了一个月了,说来惭愧,上篇博客中还说要每周都记录一点东西,前两周刚好周末有点事情加上自己还是懒惰了点,心里给自己一个接口就这么过去了。但这段时间还是有点收获的,利用业余时间玩了玩爬虫,不像之前都只是看文章并没有去实践。我一直觉得不能将自己知道的东西写出来,并让别人看得懂,就不能算真正掌握。今天就试着对我过去这段时间学习的爬虫做个总结,看看自己是否真的掌握了。写着写着,感觉废话有点多,倒不像一篇技术博客了,其实本来也不是,算是我自己的日记吧。

说起爬虫,经常在微博上看到梁斌爬些语料贡献给学术界或者卖点钱做慈善,我也曾想过玩玩,但一直不知道爬些什么数据好,于是去知乎上搜了搜,看到这篇文章—-一个知乎重度用户眼中的知乎,觉得我也可以试试,爬些知乎的用户信息,做些简单的数据分析,数据可视化,看看是不是真的有一半用户的程序员,哈哈

入门

OK,Let’s开始玩,第一步,怎么开始呢,既然要玩知乎,那就好好玩,当然去问知乎啦,如何入门 Python 爬虫?,排名第一的回答很靠谱。总结一下主要有下面几点:
1、 基本的爬虫工作原理
2、 基本的http抓取工具,scrapy
3、 Bloom Filter: Bloom Filters by Example
4、 分布式爬虫
5、 rqrq和Scrapy的结合:scrapy-redis
6、 后续处理,网页析取(grangier/python-goose · GitHub),存储(Mongodb)

第一条,爬虫原理,学生时代搞过两年时间的协议栈开发和测试,虽然写的代码不多,但TCP、UP、IP等相关的RFC还是读过一些的,整个协议栈的代码还是读过几遍的,从链路层到应用层也都调试过N遍,还为该协议栈开发了一些BSD socket标准接口,gethostbyname、gethostbyaddr之类的。所以跟协议有关的没什么大问题。(说到这里,现在回过头来看,读研没什么好的,除了参与这个协议栈项目。除了协议本身,更多的是一些其他技能,当时很多问题百度根本找不到任何资料,还有那些RFC,让我现在一遇到问题基本都是先google,然后直接官网看文档,现在所在单位很多技术人员都没有这个习惯甚至可以说没有这个能力,当然他们有很多其他地方比我厉害。当年在Linux下用Vim+GDB敲C敲得飞起,让我现在用Mac真是如鱼得水。擦,,,扯远了

第二条,scrapy,久闻大名,Python写的框架,当年在Cisco实习时业余时间学了基础,后来去Synopsys实习的时候用Python做了个小项目,所以语言不是问题。OK,那就从scrapy开始玩吧,我向来都是官网Doc先简单过一遍,大约就两个晚上的时间过了一下,知道了Spider,Item,Selector,Downloader,Pipeline是做什么的,根据demo玩了玩,确实能抓下页面并解析出一些东西。之前写Python都是在sublimme写,用PDB调试的,这次玩了玩PyCharm(How to use pycharm to debug scrapy projects帮我解决了一个问题)。现在然后就开始来爬知乎咯,第一步就卡住了,知乎的页面需要登录认证的,我当然第一时间想到发个Post请求过去或者在每次请求页面的时候都带着Cookie,但我就是这么好学,登录认证问题绝对是玩爬虫的人都遇到过的,业界肯定有人总结过解决方案,于是我就看了下面几篇文章,取其精华解决了登录问题。

后来看了MSpider Githubzhihu-scrapyjavachen/scrapy-zhihu-github的代码,借鉴了一些东西,能够从一个用户开始,解析出他的信息(名称,性别,职业,城市等),然后获得他的关注者/被关注者列表,生成新的请求,然后一直这样爬下去。到这,算是完成一个milestone了。

存储

数据是能抓下来并解析成JSON了,但用什么来存储呢,之前用过的都是关系型数据库,NoSQL,NewSQL听说很久了,一直没把玩过,那就乘这个机会玩玩咯,而且NoSQL后面做数据分析会方便些(说实话为什么会方便些,技术层面我并不是太了解,但平时喜欢逛技术社区,什么大数据,数据分析等文章翻翻一些关键字眼还是会留下些印象的)。于是就选择了MongoDB,别问我为什么,就是玩。第一步,官网Doc,花了一个晚上CRUD部分和Data Model细看了一遍,边看边操作。其他部分简单过了一下。发现很适合我的爬虫,就是一个JSON往里一丢就可以。于是,又去开源世界把noplay/scrapy-mongodb代码看了一遍,简单,一个pipeline拿来改改就可以用了,跑了一晚上测了一下,一天大概可以抓20W条数据。第二个milestone完成了

动态内容

知乎关注者/被关注者列表是瀑布流的形式,一次最多只能加载20条数据,每次翻页会通过AJAX去请求下一页数据。我当然知道是可以构建一些POST请求去获得数据的,但具体的代码怎么写,用什么函数,我就参考了Can scrapy be used to scrape dynamic content from websites that are using AJAX,第三个小milestone完成。

进阶学习

爬数据,解析,存储,一切都OK了,接下来就想玩玩分布式。这时发现我对scrapy浅薄的了解已经不能支撑我的欲望了,怎么办呢,学习呗。于是我开始看scrapy的源码,发现整个爬虫框架中的每个部门可扩展性都是很强的,都可以通过在setting文件中通过配置进行替换(后来用的scrapy-redis其实也就是重写了一个shceduler,每次请求都从redis队列中去取,然后写了个spider的基类,让每次生成的请求都放到redis队列中)。读代码的过程中很快就遇到了问题,里面用了很多defered,inlinecallbacks,看得云里雾里的,于是我又去看了twisted的相关材料,这里感谢luocheng/twisted-intro-cn

分布式爬虫

首先,选定开源库scrapy-redis. redis又是没玩过的东西,只知道是一个内存数据库。于是又是官网逛了逛,学会了初步使用,就是一些List,Set,Map之类的操作。然后看了scray-redis的源码,当中主要有三个地方用到了redis

最后在3台机器上测了一下,功能是OK,但性能和稳定性并不是很理想,也算是一个milestone吧。

小结

存在的问题,分布式爬虫稳定性和性能都不能满足我的需求,初步分析,有以下两个原因

  • request请求增长太快,每爬一个页面解析一个用户的信息,就会产生几十个甚至上千个新的请求(一个用户关注N多人或被N多人关注)。导致redis队列一下就被撑爆,内存用光,虽然redis会定时的写磁盘,但终归是一个很大的问题。具体方案后面有时间再细看。
  • 重复请求过滤,Scrapy-redis里仅仅是通过一个redis的set来过滤重复请求,很耗内存,效率不高。后面会仔细看看bloomFilter算法,看看有没有更好的方案。

对自己的期望,毕业到现在,我对自己的评价是涉猎挺广,什么东西都知道一点,学习能力强,什么东西都能很快上手并做出来,但深度不够。这两个问题就是带有点深度的问题,希望能够很好的解决。

参考文章