• 爬虫框架设计


    最近的一个项目是写一个爬虫框架,这个框架主要采用Master-Slave的结构,Master负责管理要爬取的Url和已经爬取过的Url,Slave可以有多个,主要负责爬取网页内容,以及对爬取下来的网页内容进行持久化的工作。整个项目用Thrift作为RPC通信框架。

    1. 爬虫流程

    如果是一个单机版的爬虫,其实代码非常简单:

    Initialize:
        UrlsDone = ∅
        UrlsTodo = {‘‘yahoo.com/index.htm’’, ..}
    
    Repeat:
        url = UrlsTodo.getNext()
        ip = DNSlookup( url.getHostname() )
        html = DownloadPage( ip , url.getPath() )
    
        UrlsDone.insert( url )
            newUrls = parseForLinks( html )
    
        For each newUrl
            If not UrlsDone.contains( newUrl )
            then UrlsTodo.insert( newUrl )        

    如果需要将UrlsDone和UrlsToDo这两个数据结构交由一个Master来管理,Master的接口可以定义成如下的:

    public interface SpiderManager extends Closeable {
        /**
         * 获取一个待爬取的URL
         * @return URL
         */
        URLData poll();
    
        /**
         * 将一个待爬取的URL交给Manger
         */
        void offer(URLData url);
    
        /**
         * 将一个已经爬取的URL返回给Manager
         */
        void done(URLData url);
    
        /**
         * 判断一个URL是否已经爬取过
         * @param url
         * @return
         */
        boolean isDone(URLData url);
    
        /**
         * 已经处理过的URL数量
         */
        long doneSize();
    
        /**
         * 待处理的URL数量
         */
        long toDoSize();
    
    
        boolean usingAck();
    }
    

      

    2.分布式爬虫框架要解决的问题

    上述单机的版的爬虫,在数据量不大和数据更新频率要求不高的情况下,可以很好的工作,但是当需要爬取的页面数量过多,或者网站有反爬虫限制的时候,上述代码并不能很好的工作。

    例如通用的搜索爬虫需要爬取很多网页的时候,就需要多个爬虫来一起工作,这个时候各个爬虫必然要共享上述两个数据结构。

    其次,现在很多网站对于爬虫都有限制,如果要是爬取的过于频繁,会被封Ip,为了应对这种情况,对应的策略是休眠一段时间,这样的话,又浪费了CPU资源。

    最后,当要求实现不同的爬取策略,或者统一管理爬虫作业生命周期的时候,必然要一个Master来协调各个Slave的工作。

    3. 设计实现

    3.1 Master:

    我们框架的主节点称为WebCrawlerMaster,针对不同的爬虫任务,WebCrawlerMaster会生成不同的WebCrawlerManager,WebCrawlerManger的功能是管理UrlsToDo和UrlsDone两个数据结构。Master主要的功能是管理WebCrawlerManager的实例,并且将不同的请求路由到对应的WebCrawlerManager上去。

    对于Master来说,最主要的组件是一个叫做MetaDataHolder的成员,它主要用来管理元数据信息。为了加强系统的健壮性,这部分信息是一定需要持久化的,至于持久化的选择,可以是Redis,或者关系型数据库,甚至写文件都可以。如果用Mysql来做持久化的工作,则需要做应用层的cache(通常用一个HashMap来实现)。

    3.2 数据结构

    对于一个CrawlManager,它主要管理两个数据结构UrlToDo,和UrlDone,前者可以抽象成一个链表,栈或者有优先级的队列,后者对外的表现是一个Set,做去重的工作。当定义出ADT(abstract data type)以后,则可以考虑出怎么样的去实现这个数据结构。这样的设计方法其实和实现一个数据结构是一样的,只不过当我们实现数据结构的时候,操作的对象是内存中的数组和列表,而在这个项目中,我们操作的对象是各种存储中提供给我们的功能,例如Redis中的List、Set,关系型数据库中的表等等。

     

    4. 后记

    这次的爬虫框架,从最开始的伪代码来看,是很简单的事情,但是一旦涉及到分布式的环境和系统的可扩展性,要真的实现起来,还是需要考虑到一些额外的东西,例如并发状态下共享数据结构的读写、系统的高可用等等,但是我觉得这个项目真正让我满意的地方,是通过合理的数据结构行为层面的抽象,让这个爬虫系统有着很强的扩展性。例如现在默认的UrlToDo是一个FIFO的队列,这样的话,爬虫实际上是按照BSF的策略去爬取的。但是当UrlToDo配置成一个LIFO的stack以后,爬虫实际上按照DSF的策略去爬取的,而这样的变化,只需要的更改一下请求新的WebCrawlerManager的参数,爬虫的业务代码并不需要任何的修改。

  • 相关阅读:
    管理业务IT从业人员转型生产管理1
    坐标系基准面地图投影系列介绍(二)_ 地理坐标系
    优化UVA11401(Triangle Counting)
    最小匹配hdu 3991 Harry Potter and the Present II
    解析xml——采用Jdom与dom4J方式写入xml文档
    解析xml——采用Jdom与dom4J方式读xml文档
    解析xml笔记总纲
    使用zoom.js 给博客园的图片添加点击图片放大功能
    Hello China V1.75版本运行截图
    基于visual c++之windows核心编程代码分析(26)实现文件关联
  • 原文地址:https://www.cnblogs.com/javanerd/p/5121472.html
Copyright © 2020-2023  润新知