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. Go切片的操作

    package main import "fmt" //切片的操作 func main() { //创建slice var s []int //zero value for sli ...

  2. 基于神经网络的embeddding来构建推荐系统

    在之前的博客中,我主要介绍了embedding用于处理类别特征的应用,其实,在学术界和工业界上,embedding的应用还有很多,比如在推荐系统中的应用.本篇博客就介绍了如何利用embedding来构 ...

  3. 洛谷P1757 通天之分组背包

    题目背景 直达通天路·小A历险记第二篇 题目描述 自01背包问世之后,小A对此深感兴趣.一天,小A去远游,却发现他的背包不同于01背包,他的物品大致可分为k组,每组中的物品相互冲突,现在,他想知道最大 ...

  4. hdu 4801模拟题

    /* 模拟: 注意:实质上一次魔方的一半要变化 用c++超内存 用g++过了 */ #include<stdio.h> #include<string.h> #include& ...

  5. hdu 2795线段树

    #include<stdio.h> #define N 200005 int h,w,n; struct node { int x,y,max; }a]; int mmax(int e,i ...

  6. POJ1256 Anagram

    Time Limit: 1000MS   Memory Limit: 10000KB   64bit IO Format: %lld & %llu Submit Status Descript ...

  7. 洛谷——P2916 [USACO08NOV]为母牛欢呼Cheering up the Cows

    https://www.luogu.org/problem/show?pid=2916 题目描述 Farmer John has grown so lazy that he no longer wan ...

  8. [Bzoj4817] [Sdoi2017]树点涂色 (LCT神题)

    4817: [Sdoi2017]树点涂色 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 629  Solved: 371[Submit][Status ...

  9. 【OPPO主题制作系列 - 01】-- 写个小工具自动打包Theme文件

    参考OPPO主题设计师站: http://dev.theme.oppomobile.com/user/user_start 想要打包成Theme文件,必须把需要打包的文件夹拖到oppo-themepa ...

  10. CheckStyle: 解决Unicode导致LineLength出错的问题

    在checkstyle.xml中,加上如下代码: <?xml version="1.0" encoding="UTF-8"?> <module ...