在上周写完用scrapy爬去知乎用户信息的爬虫以后,github上star个数一下就在公司小组内部排的上名次了,我还信誓旦旦的跟上级吹牛皮说若是再写一个,都很差意思和你再提star了,怕大家伤心。上级不屑的说,那就写一个爬虫爬一爬github,找一找python大牛,公司也正好在找人。临危受命,格外激动,当天就去研究github网站,琢磨怎么解析页面以及爬虫的运行策略。意外的发现github提供了很是nice的API以及文档文档,让我对github的爱已经深刻骨髓。python
说了这么多废话,讲讲真题吧。我须要下载github用户还有他们的reposities数据,展开方式也很简单,根据一个用户的following以及follower关系,遍历整个用户网就能够下载全部的数据了,据说github注册用户才几百万,一下就把全部的数据爬下来想一想还有点小激动呢,下面是流程图:git
这是我根据这个流程实现的代码,网址:https://github.com/LiuRoy/github_spider程序员
看到这么简单的流程,心里的第一想法就是先简单的写一个递归实现呗,要是性能差再慢慢优化,因此初版代码很快就完成了(在目录recursion下)。数据存储使用mongo,重复请求判断使用的redis,写mongo数据采用celery的异步调用,须要rabbitmq服务正常启动,在settings.py正确配置后,使用下面的步骤启动:github
celery -A github_spider.worker worker loglevel=info
启动异步任务python github_spider/recursion/main.py
启动爬虫由于每一个请求延时很高,爬虫运行效率很慢,访问了几千个请求以后拿到了部分数据,这是按照查看数降序排列的python项目: redis
这是按粉丝数降序排列的用户列表 并发
做为一个有追求的程序员,固然不能由于一点小成就知足,总结一下递归实现的几个缺陷:异步
针对这种I/O耗时的问题,解决方法也就那几种,要么多并发,要么走异步访问,要么左右开弓。针对上面的问题2,我最开始的解决方式是异步请求API。由于最开始写代码的时候考虑到了这点,代码对调用方法已经作过优化,很快就改好了,实现方式使用了grequests。这个库和requests是同一个做者,代码也很是的简单,就是讲request请求用gevent作了一个简单的封装,能够非阻塞的请求数据。scrapy
可是当我运行以后,发现程序很快运行结束,一查发现公网IP被github封掉了,当时心中千万只草泥马奔腾而过,没办法只能祭出爬虫的终极杀器--代理。又专门写了一个辅助脚本从网上爬取免费的HTTPS代理存放在redis中,路径proxy/extract.py,每次请求的时候都带上代理,运行错误重试自动更换代理并把错误代理清楚。原本网上免费的HTTPS代理就不多,并且不少还不能用,因为大量的报错重试,访问速度不只没有原来快,并且比原来慢一大截,此路不通只能走多并发实现了。ide
采起广度优先的遍历的方式,能够把要访问的网址存放在队列中,再套用生产者消费者的模式就能够很容易的实现多并发,从而解决上面的问题2。若是某段时间内一直失败,只须要将数据再仍会队列就能够完全解决问题3。不只如此,这种方式还能够支持中断后继续运行,程序流程图以下:性能
为了实现多级部署(虽然我就只有一台机器),消息队列使用了rabbitmq,须要建立名为github,类型是direct的exchange,而后建立四个名称分别为user, repo, follower, following的队列,详细的绑定关系见下图:
详细的启动步骤以下:
celery -A github_spider.worker worker loglevel=info
启动异步任务python github_spider/proxy/extract.py
更新代理python github_spider/queue/main.py
启动脚本队列状态图: