异步非阻塞爬虫:tornado

大约在一周前,准备用django+celery写一个关于代理IP的项目。

大致上,有如下几个目标:

1,周期性从指定网站爬去免费代理IP存到本地;

2,周期性对本地的代理IP进行检测;

3,django的admin界面可以添加定时任务,查看执行状态;

网上关于这类的项目不少,甚至已有一个第三方库,IPProxys,是七夜完成的。所以,这个项目的出发点,仅仅是个人的,想独立完成一个纯后端的项目,练手而已。

项目相对比较简单,在设计好IP数据所需要的表结构后,就可以着手IP抓取,主要是django+celery的开发,以及requests写爬虫,以及检测。

本着做一个友好的爬虫的理念,在抓取数据时,采取的是,单线程,阻塞的,并且控制访问间隔,来抓取西刺网站的代理IP,当然,后续还可以添加其他网站的零碎爬虫。

接着,是检测代理IP是否可用,原本是想着用爬取数据时所用的方法检测,但是,单线程,阻塞,所耗时间太长。所以借鉴了tornado写爬虫的官方demoe(github上有),完成了一个异步的,非阻塞的,爬虫来进行检测,检测的效率确实提升很大,但是,也并非想的那么好,2000左右的的代理IP信息,大约要15分钟左右。但同事说,也不算慢了。我猜测,并不够快的主要的原因,可能有3点:

第一,tornado的异步非阻塞特性没有完全的用好,tornado对自己而言,并不能灵活使用,还需要进一步的练习,以及优化;第二,在从数据库获取数据时,用的是django的orm模型,这个是阻塞的,可以交由torndb做,这个是支持tornado的特性的,而且本身存放代理IP的表结构很简单,自己写sql语句并不复杂;第三,办公的网络环境或者本机的性能,但,应该不是主要原因。

我在想,如果对库里所拥有的代理IP分类,交付给多个任务进行检测的话,效率可以提升点吧?比如,把前一次检测是有效和前一次检测无效分开,让两个任务的周期不同。

中途遇到两个问题:

1,关于数据一致性;

2,关于tornado的curl_httpclient使用代理时,无法找到ssl证书的错误;

第一个问题,是来源于,我的目标。目标,定期检测,会随着我库里的代理IP数据变多而逐渐增加时间。那么就有可能会生一个现象:前一个任务没有做完,后一个任务开始,导致最后可能所有的worker都被一个类型的任务占据?显然这不是我想要的。思索之下,了解到了锁的机制,运行的任务获取锁,运行完毕时释放锁。使用redis的迸发锁,它成功解决了我当前问题。注:python中操作redis时,set方法支持的参数里面,有个nx,当它为True时,如果库里已经存在了键,,将不执行赋值操作,并且返回False。所以,锁的机制能够很好解决此处的问题,当然,也肯定能解决更多的迸发问题。

第二个问题,使用tornado时,如果要用代理IP(检测代理IP是否有效),是需要用curl_httpclient.CurlAsyncHTTPClient()的。它依赖与pycurl这个第三方库。此处,在这个库的使用部分,提示了本地的ssl证书未找到的错误,导致无法检测代理IP可用性。搜索到的文献相对较少,可能因为用tornado写爬虫的人也并不多,所以,我开始着手解决这个问题。

难度是有的,因为tornado的代码并不好理解,但最后还是通过全文搜索,找到了关键的代码。尝试在源代码中添加了解决pycurl ssl证书问题的代码后,问题解决了。但是,源代码本身最好还是不要改动。所以我写了一个继承于CurlHTTPClient的类,overwrite关键的函数,_curl_create(),来实现目的:

所以,大的问题已经解决了。

从运行一下午的状态来看,爬取到的数据,在检测时,有效的代理十不存一。如果有公司的爬虫项目,肯定还是用正规的代理好,自己的现目可以跑着,放在云服务器上,折腾点自己的小玩意儿。

如果效果可以,也可以再写一个api,面向公众。

圣诞节快乐吖

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20171225G0X2CR00?refer=cp_1026

同媒体快讯

  • sphinx生成项目文档

    2018-10-20

相关快讯

扫码关注云+社区