Cola:一个分布式爬虫框架
由于早先写的WeiboCrawler问题很多,而且当时我有提到,其实可以实现一个通用的爬虫框架。最近由于要抓取新的数据,于是我就写了这个cola。下面的文字来自wiki。
Cola是一个分布式的爬虫框架,用户只需编写几个特定的函数,而无需关注分布式运行的细节。任务会自动分配到多台机器上,整个过程对用户是透明的。
依赖
由于Cola配置文件使用的yaml,所以Cola只依赖于pyyaml,安装easy_install或者pip工具后,则可以:
pip install pyyaml
安装
下载或者用git clone源码,假设在目录/to/pth/cola,将该路径添加到Python path中。
一种简单的方法是在site-packages中添加pth文件。site-packages因系统而异,如果是windows,假设python装在C:\python27,那么就是C:\python27\Lib\site-packages;如果是linux,那么应该是/usr/local/lib/pythonX.X/dist-packages。
在site-packages下新建一个cola.pth文件,里面写上路径:/to/path/cola。
运行
Cola集群需要一个master和若干个worker,对于每台机器,只能启动一个worker。但是,集群不是必须的,在单机模式下亦可以运行。
Cola目前自带了两个爬虫,分别是维基百科和新浪微博。在项目根目录下的contrib中。
下面就wiki为例,分别说明如何在单机和分布式环境下运行。
依赖
无论是维基百科还是新浪微博的实现,数据都存放在MongoDB中,所以要确保MongoDB的安装。
在wiki下的wiki.yaml和sina下的sina.yaml中可以配置MongoDB的主机和端口。
维基百科和新浪微博实现依赖于下面的几个包:
- mechanize
- python-dateutil
- BeautifulSoup4
- mongoengine
- rsa(仅新浪微博需要)
可以使用pip或者easy_install来安装。
单机模式
单机模式非常简单,只需运行contrib/wiki/__init__.py即可。
cd /to/path/cola/contrib/wiki python __init__.py
要运行新浪微博的爬虫,需要在sina.yaml中配置登录的用户名和密码。这里要注意,要保证这个用户名和密码在登录时不需要验证码。
分布式模式
首先需要启动cola master和cola workers。分别运行根目录下bin中的start_master.py和start_worker.py
启动cola master:
cd /to/path/cola python bin/start_master.py --data /my/path/data
如果不指定--data,那么数据文件会防止在项目根目录下的data文件夹中。
启动cola worker:
python bin/start_worker.py --master --data /my/path/data
--data选项同master。如果不指定master,会询问是否连接到本机master,输入yes连接。
最后使用bin下的coca.py来运行指定的Cola job:
python bin/coca.py -m -runLocalJob /to/path/cola/contrib/wiki
-runLocalJob选项是要运行的job所在文件夹的绝对路径。输入命令后,该job会被提交到Cola集群来运行。
停止Cola Job或集群
停止整个集群,则可以运行:
python bin/coca.py -stopAll
而停止一个Job,则需要查询得到Job的名称:
python bin/coca.py -showRunningJobsNames
得到名称后,再运行:
python bin/coca.py -stopRunningJobByName [job name]
编写自定义Cola Job
见wiki编写自定义Cola Job。
问题
Cola还不够稳定,目前会处于持续改进的状态。且Cola还没有在较大规模的集群上测试,但是接下来我会把Cola应用到新项目中,并逐步完善。也希望大家也能给我反馈,并帮助改进。
Todo
- 实现一个web接口,可以查看运行的cola job以及运行情况
- 实现一个opener能够运行JS代码和执行AJAX请求。
- 支持增量抓取机制。
- 简化安装,支持easy_install或者pip安装。增加解决依赖库安装的机制。
用tornado搭建日志服务器
对于一个分布式应用,一个计算节点上可能产生一些日志,而为了便于将这些节点日志收集并分析,我们可以选择除了在本地生成日志外,还将某些级别的日志发送到日志服务器。
Python的logging模块可以用来对日志进行一些操作,其接口和著名的Java日志库log4j类似。logging.handlers支持一系列的日志处理方式,其中发送到日志服务器可以通过SocketHandler或者HttpHandler,不过本文既然选择了tornado,还是使用HttpHandler的方式。
首先,是分布式节点的部分。首先,我们通过get_logger方法得到logger对象,并向其添加HttpHandler。
import logging logging_host = '127.0.0.1' logging_port = 8888 logging_add_url = '/log/add/' def get_logger(): logger = logging.getLogger() http_handler = logging.handlers.HTTPHandler( '%s:%s' % (logging_host, logging_port), logging_add_url, method='POST' ) http_handler.setLevel(logging.ERROR) logger.addHandler(http_handler) return logger
代码还是很简单的,HTTPHandler接受三个参数,一个是服务器的host,日志发送到的url,以及是GET还是POST方式。这里,省去了添加本地日志的过程。
一个简单的分布式新浪微博爬虫
Update 2013/6/17:WeiboCrawler之前已经说过不再维护了,现在请大家关注我的新项目——cola。Cola是一个分布式的爬虫框架,支持包括新浪微博(网页版)的抓取。而且目前实验下来,长时间抓取也不会被封锁。项目地址:https://github.com/chineking/cola
由于实验室实验需要,我写了个简单的新浪微博爬虫,来从新浪微博上获取一些实验所需要的数据。现在实验数据抓取部分已经比较稳定,所以我把这些代码放出来,说不定会对一些同学有帮助吧。
至于为什么不用新浪微博API,以及整个分布式爬虫的架构、安装和部署、以及缺陷等等都已经在wiki中进行了解释。
以后,这个程序应该不会再去维护和更新了,但是,我觉得这个程序稍作修改,还是可以适用于其他的特定目标的爬虫的,所以如果有同学需要修改这个项目,可以fork代码,或者联系我成为项目的contributor。
项目地址在BitBucket上——https://bitbucket.org/chineking/weibocrawler。
Update(2013/1/21):最新的weibo.cn抓取不了数据了,改动已经提交,请大家pull最新代码。
使用GAE的一些感受
最近个人的一个小项目部署在GAE上,项目写了也有一个多月,所以这篇文章就主要说说这一个多月来使用GAE的一些感受,也算是给想选择使用GAE的同学一些建议把。
其实GAE一点都不是什么新奇的东西,推出也有好几年了,不过由于众所周知的原因,国内访问不是很顺畅,appspot.com更是断得彻底,所以很多程序猿都不是很了解。其实,GAE就是云计算当中一个重要分支PaaS(平台即服务)的重要代表,你只需按照GAE提供的一系列API写好应用,upload上去,就可以访问了,服务器的事情都可以交给Google来处理,包括云的弹性和数据的存储等等。而Google方面也提供用户一些基础的配额,在这些配额之下,用户是无需任何花费的,不过如果应用如果需要更多的资源,那就需要付费了,好的地方是,你的应用瓶颈在哪里,你就可以只为那部分付费,这看起来确实节约了开销。
GAE访问不畅,国内类似的产品倒不少,比较知名的就是新浪的SAE和百度的BAE,这俩从名字上就明白了。SAE和GAE很类似,BAE看起来也差不多,不过,现在还在邀请使用的阶段,而我也没能拿到邀请码,所以哪位同学有多余的,可以分享给我一份,不胜感激:)
话休絮烦。使用下来的体会主要有以下几点:
使用GAE就意味着你的应用被限定在它的框架中,这是一把双刃剑。好处主要是方便,操作数据库可以使用datastore API而无需自己配置数据库;taskqueue也直接有API使用,异步的任务队列很轻松就搞定,而无需使用诸如Celery+RabbitMQ的解决方案;Channel API提供了类似客户端和服务器长连接的技术,它复用了GTalk的组件,会适配浏览器而选择使用WebSocket抑或是长轮询,你无需去了解它们的细节。这些当然只是例子,还有诸如cron job等等,它极大得简化了开发人员的工作,而不是让程序猿们花大量时间在配置上。
坏处也就是显而易见的了,由于大量使用GAE提供的接口,应用的可移植性就变得很差,如果一旦由于各种原因放弃使用GAE,整个程序面临着重写,当然将问题减少到最小的方法是使用适配器模式来做一层封装,不过还是有较大的工作量。
第二就是关于配额的的问题。其实在GAE部署应用了之后才能深刻体会到配额的数量真的不多,所以不可避免的做法就是在应用里处处精打细算,至少不能浪费。
首先是datastore的配额问题。在其免费配额中关于读操作有两项:“Datastore Read Operations”和:“Datastore Small Operations”,他们都有5万次/天的配额,不过千万别觉得Google慷慨,这个其实是很不经用的。不过相信很多人不明白什么是read和small,这里就先简单做个介绍。
对于datastore,每个要存储的model实体(相当于关系数据库中的行),都有个必有的key和可选的parent属性,通过它们就可以找到一个model实体,而parent是极其重要的,如果应用中用到了事务,不论是否跨model,只有拥有相同的祖先(ancestor)才可以完成一次事务。对于譬如一次查询操作,我们查询到了十个结果并返回这些model实体,那么我们就使用了11次read操作;更极端的是,如果查询的时候使用了offset,就是跳过了offset个结果,这些要是要算入到read操作中的,所以比如说取跳过50个的10个结果,这实际上就是61次read操作,所以开销是极大的。解决方法就是使用游标(cursor),每次查询的时候拿到上次查询的游标,从游标开始取10个,这样就还是11次操作。
那么什么是small操作呢,刚刚提到了model的key属性,我们通过key得到一个model实体,这就是1次small操作;还有就是count操作,比如说我们得到结果是100,那么就是1次的read操作+100次的small操作,因为计算数量不需要取到数据,只需要知道key就可以了。而我们查询结果的时候,可以只查询得到key,再通过key去得到model实体,这样就减少了read操作,但也相应增大了small操作。不过无论如何,都要记住游标是很重要的东西,它其实只是一个很长的字符串,所以在网页应用中,我们可以渲染到页面中,然后在之后的查询时,作为参数传递。
使用游标确实能减少配额,不过不能一劳永逸,那么怎么办呢?相信很多同学都想到了,当然是使用缓存(memcache)。因此可以说缓存在GAE中非常重要,因为缓存和配额没有关系,它的使用仅仅和内存的大小有关,所以几乎所有常用的数据都要放到缓存中,缓存的类型也可以包括model实体和页面等等的缓存。所以我们可以用工厂模式来操作数据库的增删查改,每个操作都和缓存结合起来。
关于配额还有一点就是Channel API,它的使用需要在用户访问时创建一个channel,而这个channel 2个小时就会失效。由于每天创建channel的个数为100,如果每次用户访问都需要创建channel,开销太大了,所以,我的做法还是将这个用户的channel缓存,并设置2个小时的失效时间。
好了,关于GAE使用的感受和心得,暂时就总结到这里。如果想到其他的,我会进行补充,以后也会对一些细节做一些分享。
Amazon S3 REST API详解
好久没有写博客,学期接近尾声,时间方面会宽松些了,其实想写的东西还挺多的。前段时间一个项目用到了几个云存储,其中就包括Amazon S3,于是这篇文章主要分享一些Amazon S3 Rest API的经验(Amazon S3还包括SOAP API)。
首先还是要先说一下Amazon S3,全称是Amazon Simple Storage Service。EC2和S3是Amazon最早推出的两项云服务。在传统的计算机领域,主要包括计算、存储、网络这几个方面, 在云计算时代,前两者分别对应虚拟化和cloud storage,由此可以显现出Amazon EC2和S3的重要性。如今随着云计算的大红大紫,也有很多使用Amazon S3的例子,典型的有Dropbox,还有之前被FB收购的Instagram,其照片存储就使用的S3。
关于REST,这也是比较火的一种Web服务架构。简单来说,资源是由URI指定,对资源的操作包括GET、PUT、POST、DELETE和HEAD,返回结果常常是XML或者其他形式。如果你想了解更多,可以查看REST的Wiki页面。
Amazon S3的操作包括三部分:Service,Buckets和Objects。Service只包括GET操作,就是返回所有的Buckets的列表。Object顾名思义,是指存储在云端的文件,值得注意的是,S3中并没有明确的文件夹的概念,而是通过指定object的路径来实现,比如说,object可以为“photos/1.jpg”。而Bucket拥有全局名,名称由用户定义,用来存放Object,由于是全局名,所以要确保名字是别人没用过的。
访问Web服务时,Http request headers需要一些参数。主要包括:
- Date:当前UTC时间,形式为“ Wed, 01 Mar 2009 12:00:00 GMT ”。
- Content-Length: 当对Object进行操作的时候,返回内容的长度,注意不要包括headers中的内容。
- Content-MD5:用base64编码文件内容的MD5值。
- Content-Type:资源的类型,比如:text/plain。
- Host:Get Service时为“ s3.amazonaws.com ”。在对bucket和object进行操作时,例如bucket的名字是“bucketname”,那么Host就是“bucketname.s3.amazonaws.com”。
- x-amz-meta-和x-am-开头的:包括Amazon定义的一些元数据和一些特定的header。后面如果出现会提到。
- Authorization:这个是最重要的,主要作用是签名,Amazon根据的请求计算出一个签名值和这里计算的签名值进行比对,只有相同时,访问才是合法的。接下来对Authorization的计算方法进行详述。