编写自定义cola job

Chine King edited this page Jul 13, 2013 · 3 revisions
Clone this wiki locally

编写自定义Cola Job

get_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
>>> 

编写url patterns

对于一个要抓取的链接,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模式

非bundle模式(url)

很多时候,我们要抓取的对象只是一个个的网页,它们之间除了相互链接没有什么特别的关系,比如说维基百科,那么这个时候,我们不需要bundle模式。

对于一个url,cola从url patterns里得到这个url匹配的parser,则用这个parser来解析这个url。此时,Parser.parse函数返回的是一个列表,每个元素就是url链接。得到这些链接后,它们会被push到cola集群中作为接下来的抓取对象。

Bundle模式

对于新浪微博这种类型的网站,我们的抓取往往是从一个用户开始,首先是他的所有微博,然后是个人信息,最后是他关注的和被关注的用户。那么对于这个用户来说,其就是一个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的实例,其会从中随机选出一个来进行登录。

让Job支持单机运行

只需在结尾加上:

if __name__ == "__main__":
    from cola.worker.loader import load_job
    load_job(os.path.dirname(os.path.abspath(__file__)))