编写自定义cola job

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

编写自定义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__)))
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.