Larbin是一个用C++开发的开源网络爬虫,有一定的定制选项和较高的网页抓取速度。

下图表示了一般爬虫抓取网页的基本过程。

抓取以/Larbin.conf中的startUrl做为种子URLs开始。

下面先来看用于处理url的类

上面的类图只显示了url类可见的接口。除了基本的构造函数和私有变量的get函数,url类比较重要的函数是hashCode( ),其实现为:

/* return a hashcode for this url */

uinturl::hashCode () {

unsigned int h=port;

unsigned int i=0;

while (host[i] != 0) {

h = 31*h + host[i];

i++;

}

i=0;

while (file[i] != 0) {

h = 31*h + file[i];

i++;

}

return h % hashSize;

}

在全局变量Globle.h中,hashTable *seen用来表示抓取中出现过的URLs。按照Larbin的默认值hashSize为64,000,000,*seen是一个大小为8,000,000的char数组的hashTable结构,该char数组*table的每一个bit位可以代表一个出现过的URL。

这里使用的是一种叫做Bloom Filter的空间效率很高的随机数据结构。它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。BloomFilter的这种高效是有一定代价的:在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素误认为属于这个集合(falsepositive)。因此,Bloom Filter不适合那些“零错误”的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter通过极少的错误换取了存储空间的极大节省。(关于Bloom Filter的介绍参考了Bloom Filter概念和原理从哈希存储到Bloom Filter。)

设定*table数组bit位的函数实现如下:

/* add a newurl in the hashtable

* return true if it has been added

* return false if it has already been seen */

boolhashTable::testSet (url *U) {

unsigned int code = U->hashCode();

unsigned int pos = code / 8;                   //该URL的hashCode在table中的索引

unsigned int bits = 1 << (code % 8);     //该hashCode在字节中的bit位

int res = table[pos] & bits;                     //判断对应bit位是否为1,为1时res为正数

table[pos] |= bits;                                    //将对应bit位标记为1

return !res;

}

这里只使用了一个hash函数将URL映射到*table中的相应位。按照Bloom Filter的元素判断方法,当对应bit位为1时,表示该URL已经见过。

对于网页内容的简单去重使用的是hashDup *hDuplicate,hashDup这一结构同样基于BloomFilter,其相关实现为:

/*set a page in the hashtable

* return false if it was already there

* return true if it was not (ie it is new)*/

bool hashDup::testSet(char *doc) {

unsigned int code = 0;

char c;

for (uint i=0; (c=doc[i])!=0; i++) {

if (c>'A' && c<'z')

code = (code*23 + c) % size;

}

unsigned int pos = code / 8;

unsigned int bits = 1 << (code % 8);

int res = table[pos] & bits;

table[pos] |= bits;

return !res;

}

显然这里的网页内容去重过于简单,只能区分指向页面内容完全相同的不同链接。

为了分析和处理抓取回的网页和robot文件,Larbin中设计了file类, html类和robots类继承了file类。下面的类图显示了这几类的关系和相关函数。

html类中的inputHeaders()函数用于处理获得的网页文件的头部,包括链接、状态等。endInput函数调用其他私有函数进行一系列关于网页内容的分析和链接的提取管理操作。endInput先调用global::hDuplicate->testSet( )检测网页是否重复,再调用parseHtml()进行网页内容的分析。parseHtml( )中比较重要的是调用parseTag( )分析html标签,然后调用parseContent(action)针对标签内容进行URL的处理。parseContent(action)中调用了manageUrl( )函数,manageUrl( )调用/fetch/checker.cc中的check( )函数将URL的加入前端队列。

这些函数之间的调用关系可以在阅读源码时看到。此外,处理网页的模块还会用到一些文本操作函数,它们定义在/utils/text.h中,当然也需要url类中的相关处理函数。

接下来考虑larbin的URL队列管理。*global::URLsPriority和*global::URLsPriorityWait是用于特定类型文件下载时所需的队列,这里暂不考虑。Larbin用于一般爬取时主要用到的队列包括:

PersistentFifo*URLsDisk;                 //作为URL集合的前段队列,加入新的URL

PersistentFifo*URLsDiskWait;        //提供对一定数量的前段URL的非阻塞访问,

//即当队列为空时不阻塞,而直接返回NULL

NamedSite *namedSiteList;             //使用URL的hashCode为索引的已访问站点的大表,//维护各个站点的dns属性、对应站点待抓取URL队列

Fifo<IPSite>*okSites;                         //表示队列中存在已获得主机地址的站点的待抓取URL

Fifo<NamedSite>*dnsSites;             //表示队列中存在可抓取的站点URL,但需要dns解析调用

NamedSite*namedSiteList是维护URL队列的重要数据结构。在larbin的默认设置中,它可以维护20000个站点的待抓取队列。NamedSite类的类图如下:

可以看出,NamedSite对每个站点维护着一个fifo队列。根据不同站点的dns状态,它使用newQuery()通过adns库建立新的dns异步解析服务(DNS服务模块使用了开源的adns库,http://www.chiark.greenend.org.uk/~ian/adns),同时可以通过putUrl()和putUrlWait( )来管理URL后端队列global::*okSites的URL添加。

至此,本文最开始的第一张爬虫结构图中的主要模块及实现均做了介绍。整合在一起如下图所示:

Larbin的运行过程可以描述如下:种子URL文件最初初始化*URLsDisk,读取到namedSiteList中,通过adns库调用,逐渐往Fifo<NamedSite> *dnsSites和Fifo<IPSite>*okSites内装入链接,而Fetch模块直接从Fifo<IPSite> *okSites中获得用于抓取的URL,为抓取到的网页建立hash表,以防止网页的重复抓取。然后通过html类的方法从下载到的网页中析取出新的URL,新加入前端队列的URL要求符合robots filter,并通过hash表对URL去重。一次抓取结束后进行相关的读写操作,然后通过poll函数选择适合的套接字接口,开始新的抓取。这样抓取就可以一直循环下去,直到用户终止或者发生中断。

Larbin更详细的实现和定制细节可参考源码注释。

参考资料:

1,  搜索引擎Larbin设计原理,http://blog.chinaunix.net/u/21158/showart_133214.html

2,  Larbin中的队列结构,http://blog.chinaunix.net/u/21158/showart_1106639.html

3,  Bloom Filter概念和原理,http://blog.csdn.net/jiaomeng/archive/2007/01/27/1495500.aspx

4,  Larbin项目,http://larbin.sourceforge.net/index-eng.html

c++爬虫子的更多相关文章

  1. Python爬取CSDN博客文章

    0 url :http://blog.csdn.net/youyou1543724847/article/details/52818339Redis一点基础的东西目录 1.基础底层数据结构 2.win ...

  2. Java爬虫爬取网站电影下载链接

    之前有看过一段时间爬虫,了解了爬虫的原理,以及一些实现的方法,本项目完成于半年前,一直放在那里,现在和大家分享出来. 网络爬虫简单的原理就是把程序想象成为一个小虫子,一旦进去了一个大门,这个小虫子就像 ...

  3. HDU 1049(蠕虫爬井 **)

    题意是一只虫子在深度为 n 的井中,每分钟向上爬 u 单位,下一分钟会下滑 d 单位,问几分钟能爬出井. 本人是直接模拟的,这篇博客的分析比较好一些,应当学习这种分析问题的思路:http://www. ...

  4. Node.js爬虫实战 - 爬你喜欢的

    前言 今天没有什么前言,就是想分享些关于爬虫的技术,任性.来吧,各位客官,里边请... 开篇第一问:爬虫是什么嘞? 首先咱们说哈,爬虫不是"虫子",姑凉们不要害怕. 爬虫 - 一种 ...

  5. Scrapy框架爬虫初探——中关村在线手机参数数据爬取

    关于Scrapy如何安装部署的文章已经相当多了,但是网上实战的例子还不是很多,近来正好在学习该爬虫框架,就简单写了个Spider Demo来实践.作为硬件数码控,我选择了经常光顾的中关村在线的手机页面 ...

  6. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  7. python爬取github数据

    爬虫流程 在上周写完用scrapy爬去知乎用户信息的爬虫之后,github上star个数一下就在公司小组内部排的上名次了,我还信誓旦旦的跟上级吹牛皮说如果再写一个,都不好意思和你再提star了,怕你们 ...

  8. 安卓易学,爬坑不易——腾讯老司机的RecyclerView局部刷新爬坑之路

    针对手游的性能优化,腾讯WeTest平台的Cube工具提供了基本所有相关指标的检测,为手游进行最高效和准确的测试服务,不断改善玩家的体验.目前功能还在免费开放中. 点击地址:http://wetest ...

  9. Python多线程爬虫爬取电影天堂资源

    最近花些时间学习了一下Python,并写了一个多线程的爬虫程序来获取电影天堂上资源的迅雷下载地址,代码已经上传到GitHub上了,需要的同学可以自行下载.刚开始学习python希望可以获得宝贵的意见. ...

随机推荐

  1. POJ 1635 树的最小表示法

    题目大意: 用一堆01字符串表示在树上走动的路径,0表示往前走,1表示往回走,问两种路径方式下形成的树是不是相同的树 我们可以利用递归的方法用hash字符串表示每一棵子树,然后将所有子树按照字典序排序 ...

  2. hdu 4923 Room and Moor [ 找规律 + 单调栈 ]

    传送门 Room and Moor Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Oth ...

  3. [Bzoj1030][JSOI2007]文本生成器(AC自动机)(dp)

    1030: [JSOI2007]文本生成器 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 5254  Solved: 2172[Submit][Stat ...

  4. java-过滤器(Filter)

    在javaweb开发中,项目中都会包含一些过滤器(Filter),主要用于web服务器对资源的管理控制,如静态资源文件.jsp页面访问等.我们可以使用过滤器实现一些特殊的功能,如常见的过滤敏感词汇(替 ...

  5. linux命名详解及其软件安装实例

    始于cd,ls命令 好啦,步入正题,我使用的linux连接工具为xshell,mRemoteNG,对两款工具不做介绍啦,你可以百度一下,实在不会入左上方群. 进入之后,便是上面的界面黑乎乎一片,对于初 ...

  6. Maven使用tomcat7-maven-plugin插件run时出现错误: A child container failed during start java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component

    错误如下: A child container failed during startjava.util.concurrent.ExecutionException: org.apache.catal ...

  7. hotswapagent——热更新代码而无需重启生产环境

    http://blog.csdn.net/littleschemer/article/details/51645722

  8. 【.Net Core 学习系列】-- EF Core 实践(Code First)

    一.开发环境: VS2015, .Net Core 1.0.0-preview2-003156 二解决方案: 新建项目: File --> New --> Project -->   ...

  9. 《gis空间分析及应用案例解析》培训总结

    <gis空间分析及应用案例解析>培训总结 来源:常德水情 作者:唐校准 发布日期:2014-01-02       2013年12月2630日由中国科学院计算技术研究所教育中心组织的< ...

  10. python手记(53)

    import sys import pygame from pygame.locals import * import time import math pygame.init() screen=py ...