-
Notifications
You must be signed in to change notification settings - Fork 537
编写自定义cola job
一个自定义的Cola Job,需要定义一个get_job方法,并返回一个cola.job.Job对象。
下面是Job的定义:
class Job(object):
def __init__(self, name, url_patterns, opener_cls, starts,
is_bundle=False, unit_cls=str,
instances=1, debug=False, user_conf=None,
login_hook=None):
'''Job definition'''
- name:Job的名称
- url_patterns:cola.core.urls.UrlPatterns的实例,见 编写url patterns 。
- opener_cls:cola.core.opener.Opener的派生类,默认自带了两种opener,分别是BuiltinOpener和MechanizeOpener。前者用urllib2标准库实现,后者用Mechanize库来实现。用户也可以实现自己的Opener派生类。
- starts:初始抓取的对象。
- is_bundle:是否为bundle模式,见 Bundle模式 。
- unit_cls:如果是bundle模式,就需要为cola.core.unit.Bundle的派生类。
- instances:每个job worker上爬虫的实例数。
- debug:如果为True,那么在抓取的时的错误会被抛出,导致抓取中断。在单机下运行时设置为True可以方便调试。
- user_conf:见 配置文件 。
- login_hook:见 编写登录函数 。
对于一个Cola Job,需要一个yaml配置文件,以wiki.yaml为例:
job:
db: wiki
mode: url # also can be `bundle`
size: 100 # the destination (including bundle or url) size
limit: 0 # 0 means no restrictions, if greater than 0, means webpages opened per minute.
master_port: 12102
port: 12103
instances: 2
mongo:
host: 'localhost'
port: 27017
starts:
- url: http://en.wikipedia.org/wiki/Python
- url: http://zh.wikipedia.org/wiki/Python
- db:MongoDB中的Database名称。
- mode:是否为Bundle模式,稍后会进行讲解。
- size:目标抓取的个数。
- limit:一分钟内访问网页的次数限制。这个限制是对于整个Cola集群的访问速度限制。
- master_port:job master的端口。对于每个Job来说,也是有master和worker的。因此一个cola集群的master管理多个job master,同理,一个worker上有多个job worker。如果一个集群上会运行不同的job,请设置不同的master_port和port值。
- port:job worker的端口。
- instances:一个job worker上同时运行的实例。
- mongo:MongoDB设置。
- starts:初始要抓取的对象。
下面的代码是wiki中的片段。展示如何得到cola.core.config.Config对象,并获取配置文件中的值:
import os
from cola.core.config import Config
# 注意这里获取文件的路径的方式,注意一定不要直接写路径
user_conf = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'wiki.yaml')
user_config = Config(user_conf)
然后我们就能得到配置中的值,示例:
>>> user_config.job.db
'wiki'
>>> user_config.job.limit
0
>>> user_config.job.starts
[{'url': 'http://en.wikipedia.org/wiki/Python'}, {'url': 'http://zh.wikipedia.org/wiki/Python'}]
>>> for start in user_config.job.starts:
... print start.url
...
http://en.wikipedia.org/wiki/Python
http://zh.wikipedia.org/wiki/Python
>>>
对于一个要抓取的链接,cola会从定义的url patterns里找出匹配的,并用其定义的cola.core.parsers.Parser的派生类来解析这个网页。
下面是wiki中的代码:
from cola.core.urls import UrlPatterns, Url
url_patterns = UrlPatterns(
Url(r'^http://(zh|en).wikipedia.org/wiki/[^(:|/)]+$', 'wiki_page', WikiParser)
)
一个UrlPatterns类是由多个Url类初始化的。对于一个Url,它的初始化第一个值为要匹配的url的正则表达式,第二个为名称,最后一个参数则是Parser具体的的派生解析类。
cola.core.parsers.Parser类只有一个方法:parse,但是,值得注意的是,对于是否是bundle模式,这个parse方法的返回值是不一样的。参见 Bundle模式 的说明。
很多时候,我们要抓取的对象只是一个个的网页,它们之间除了相互链接没有什么特别的关系,比如说维基百科,那么这个时候,我们不需要bundle模式。
对于一个url,cola从url patterns里得到这个url匹配的parser,则用这个parser来解析这个url。此时,Parser.parse函数返回的是一个列表,每个元素就是url链接。得到这些链接后,它们会被push到cola集群中作为接下来的抓取对象。
对于新浪微博这种类型的网站,我们的抓取往往是从一个用户开始,首先是他的所有微博,然后是个人信息,最后是他关注的和被关注的用户。那么对于这个用户来说,其就是一个bundle,cola.core.unit.Bundle类只有一个方法:urls,它返回这个bundle的初始url链接列表。
比方我定义一个新浪微博用户的bundle,urls返回这么些url(下面只是例子,真实的url不是这样的):
['http://weibo.com/uid1/weibo/page/1',
'http://weibo.com/uid1/info',
'http://weibo.com/uid1/favs/page/1',
'http://weibo.com/uid1/follow/page/1']
这些就是初始的链接,此时parse方法应该返回两个列表,第一个是url列表,第二个是bundle列表。
比如首先开始weibo.com/uid1/weibo/page/1, 这时parse方法返回“['weibo.com/uid1/weibo/page/2'], []”,此时cola会接着抓取page/2的网页,直到parse返回的第一个列表为空,这实际上是个深度遍历的过程。
对于第二个列表,比如说,cola开始抓取weibo.com/uid1/favs/page/1,这时,我们得到了一系列的微博用户,其实就是一个个的Bundle对象,此时parse方法就可以返回:“['weibo.com/uid1/favs/page/2'], [WeiboBundle(uid2), WeiboBundle(uid3)]”,这些bundle就会被push到整个cola集群中,等待接下来的抓取。
有些待抓取的网站需要登录才能访问一些页面,这是,就需要用户编写自定义的登录函数。以新浪微博为例:
def login_hook(opener, **kw):
username = kw['username']
passwd = kw['password']
# login
对于一个自定义的登录函数,接受opener和一系列的key-value值。这些key-value值是定义在配置文件中。
job:
login:
- username: username1
password: password1
- username:username2
password: password2
可以看到,这些key-value值是定义在job下的login中。
如果定义多个用户名和密码,那么对于一个job worker的实例,其会从中随机选出一个来进行登录。
只需在结尾加上:
if __name__ == "__main__":
from cola.worker.loader import load_job
load_job(os.path.dirname(os.path.abspath(__file__)))