JLiteSpider - 一个轻量级的分布式 java 爬虫框架 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
luohaha
V2EX    分享创造

JLiteSpider - 一个轻量级的分布式 java 爬虫框架

  •  
  •   luohaha 2016-10-07 12:28:46 +08:00 4825 次点击
    这是一个创建于 3292 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目地址

    项目在 https://github.com/luohaha/JLiteSpider

    特点

    这是一个强大,但又轻量级的分布式爬虫框架。 jlitespider 天生具有分布式的特点,各个 worker 之间需要通过一个或者多个消息队列来连接。消息队列我的选择是rabbitmq。 worker 和消息之间可以是一对一,一对多,多对一或多对多的关系,这些都可以自由而又简单地配置。消息队列中存储的消息分为四种: url ,页面源码,解析后的结果以及自定义的消息。同样的, worker 的工作也分为四部分:下载页面,解析页面,数据持久化和自定义的操作。
    用户只需要在配置文件中,规定好 worker 和消息队列之间的关系。接着在代码中,定义好 worker 的四部分工作。即可完成爬虫的编写。

    总体的使用流程如下:

    • 启动 rabbitmq 。
    • 在配置文件中定义 worker 和消息队列之间的关系。
    • 在代码中编写 worker 的工作。
    • 最后,启动爬虫。

    安装

    使用 maven :

    <dependency> <groupId>com.github.luohaha</groupId> <artifactId>jlitespider</artifactId> <version>0.4.1</version> </dependency> 

    直接下载 jar 包:

    点击下载

    Worker 和消息队列之间关系

    worker 和消息队列之间的关系可以是一对一,多对一,一对多,多对多,都是可以配置的。在配置文件中,写上要监听的消息队列和要发送的消息队列。例如:

    { "workerid" : 2, "mq" : [{ "name" : "one", "host" : "localhost", "port" : 5672, "qos" : 3 , "queue" : "url" }, { "name" : "two", "host" : "localhost", "port" : 5672, "qos" : 3 , "queue" : "hello" }], "sendto" : ["two"], "recvfrom" : ["one", "two"] } 

    workerid : worker 的 id 号
    mq : 各个消息队列所在的位置,和配置信息。name字段为这个消息队列的唯一标识符,供消息队列的获取使用。host为消息队列所在的主机 ip ,port为消息队列的监听端口号( rabbitmq 中默认为 5672 )。qos为消息队列每次将消息发给 worker 时的消息个数。queue为消息队列的名字。host+port+queue可以理解为是消息队列的唯一地址。
    sendto : 要发送到的消息队列,填入的信息为mq中的name字段中的标识符。
    recvfrom : 要监听的消息队列,消息队列会把消息分发到这个 worker 中。填入的信息同样为mq中的name字段中的标识符。

    消息的设计

    在消息队列中,消息一共有四种类型。分别是 url , page , result 和自定义类型。在 worker 的程序中,可以通过 messagequeue 的四种方法(sendUrl, sendPage, sendResult, send)来插入消息。 worker 的 downloader 会处理 url 消息, processor 会处理 page 消息, saver 会处理 result 消息, freeman 会处理所有的自定义的消息。我们所要做的工作,就是实现好 worker 中的这四个函数。

    Worker 接口的设计

    JLiteSpider 将整个的爬虫抓取流程抽象成四个部分,由四个接口来定义。分别是 downloader , processor , saver 和 freeman 。它们分别处理上述提到的四种消息。

    你所需要做的是,实现这个接口,并将想要抓取的 url 链表返回。具体的实现细节,可以由你高度定制。

    1. Downloader:

    这部分实现的是页面下载的任务,将想要抓取的 url 链表,转化(下载后存储)为相应的页面数据链表。

    接口设计如下:

    public interface Downloader { /** * 下载 url 所指定的页面。 * @param url * 收到的由消息队列传过来的消息 * @param mQueue * 提供把消息发送到各个消息队列的方法 * @throws IOException */ public void download(Object url, Map<String, MessageQueue> mQueue) throws IOException; } 

    你同样可以实现这个接口,具体的实现可由你自由定制,只要实现download函数。url是消息队列推送过来的消息,里面不一定是一条url,具体是什么内容,是由你当初传入消息队列时决定的。mQueue提供了消息发送到各个消息队列的方法,通过mQueue.get("...")选取消息队列,然后执行 messagequeue 的四种方法(sendUrl, sendPage, sendResult, send)来插入消息。

    2. Processor:

    Processor是解析器的接口,这里会从网页的原始文件中提取出有用的信息。

    接口设计:

    public interface Processor{ /** * 处理下载下来的页面源代码 * @param page * 消息队列推送过来的页面源代码数据消息 * @param mQueue * 提供把消息发送到各个消息队列的方法 * @throws IOException */ public void process(Object page, Map<String, MessageQueue> mQueue) throws IOException; } 

    实现这个接口,完成对页面源码的解析处理。page是由消息队列推送过来的消息,具体格式同样是由你在传入时决定好的。mQueue使用同上。

    3. Saver:

    Saver实现的是对解析得到结果的处理,可以将你解析后得到的数据存入数据库,文件等等。或者将 url 重新存入消息队列,实现迭代抓取。

    接口的设计:

    public interface Saver { /** * 处理最终解析得到的结果 * @param result * 消息队列推送过来的结果消息 * @param mQueue * 提供把消息发送到各个消息队列的方法 * @throws IOException */ public void save(Object result, Map<String, MessageQueue> mQueue) throws IOException; } 

    通过实现这个接口,可以完成对结果的处理。你同样可以实现这个接口,具体的实现可由你自由定制,只要实现download函数。result是消息队列推送过来的结果消息,具体的格式是由你当初传入消息队列时决定的。mQueue的使用同上。

    4. Freeman:

    通过上述的三个流程,可以实现爬虫抓取的一个正常流程。但是jlitespider同样提供了自定义的功能,你可以完善,加强,改进甚至颠覆上述的抓取流程。freeman就是一个处理自定义消息格式的接口,实现它就可以定义自己的格式,以至于定义自己的流程。

    接口的设计:

    public interface Freeman { /** * 自定义的处理函数 * @param key * key 为自定义的消息标记 * @param msg * 消息队列推送的消息 * @param mQueue * 提供把消息发送到各个消息队列的方法 * @throws IOException */ public void doSomeThing(String key, Object msg, Map<String, MessageQueue> mQueue) throws IOException; } 

    通过实现doSomeThing函数,你就可以处理来自消息队列的自定义消息。key为消息的标记,msg为消息的内容。同样,通过mQueuesend方法,可以实现向消息队列发送自定义消息的操作。(需要注意,自定义的消息标记不能为:urlpageresult。否则会被认为是jlitespider的保留消息,也就是由上述的三个接口函数来处理。)

    总结说明

    jlitespider的设计可能会让您有些疑惑,不过等您熟悉这一整套的设计之后,您就会发现jlitespider是多么的灵活和易于使用。

    更多的例子在项目主页 : https://github.com/luohaha/JLiteSpider

    2 条回复    2016-10-07 12:42:43 +08:00
    slixurd
        1
    slixurd  
       2016-10-07 12:34:20 +08:00
    用 rabbitmq 来分发抓取到的页面源代码不太好吧?
    尤其是像 rabbitmq 这样随着 payload 大小,发送速度指数下降的中间件
    感觉随便跑一下就会被限流...
    luohaha
        2
    luohaha  
    OP
       2016-10-07 12:42:43 +08:00
    @slixurd 多谢关注啊,这个问题我也考虑到了。但是我最后想的是,使用者可以自由的发挥啊,可以不通过 mq 来存储页面源代码,而是直接解析存入最终结果也是可以的。从 url->page->结果,这个流程只是可选项,不是一定要这么做的。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2691 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 07:21 PVG 15:21 LAX 00:21 JFK 03:21
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86