开源爬虫larbin分析

jopen 12年前
     <div id="cnblogs_post_body">     <p><span style="font-size:14pt;"><strong>1. larbin简介</strong></span><br />     larbin是一种开源的网络爬虫/网络蜘蛛,由法国的年轻人Sébastien Ailleret独立开发,用c++语言实现。larbin目的是能够跟踪页面的url进行扩展的抓取,最后为搜索引擎提供广泛的数据来源。 Larbin只是一个爬虫,也就是说larbin只抓取网页,至于如何parse的事情则由用户自己完成。另外,如何存储到数据库以及建立索引的事情 larbin也不提供。<br />   latbin最初的设计也是依据设计简单但是高度可配置性的原则,因此我们可以看到,一个简单的larbin的爬虫可以每天获取500万的网页,实在是非常高效。<br />   利用larbin,我们可以轻易的获取/确定单个网站的所有联结,甚至可以镜像一个网站;也可以用它建立url 列表群,例如针对所有的网页进行 url retrive后,进行xml的联结的获取。或者是 mp3,或者定制larbin,可以作为搜索引擎的信息的来源。</p>     <p> </p>     <p><span style="font-size:14pt;"><strong>2. 高效的larbin</strong></span><br />     简介中提到larbin是一个非常高效的爬虫,但没有说明为什么。这里尝试列出几个原因。此外,由于接触爬虫的时间尚短,没发现的地方,希望各位能补充下。<br /> <strong>a. 节省空间的hash容器</strong>。在larbin中,hash的主要用途是判重,因此没必要将元素的值记录到hash表中。于是就使用一个位图保存hash code,根据位图某个位是否为1,判断某个元素是否在hash表中。当要插入一个新元素时,就将hash code对应的位置1。这样说可能不容易明白,举个例吧。假设int为32位,位图为int bitmap[100],元素A的hash code为120,将元素A插入到hash容器就是将bitmap的第120位置1,即bitmap[120/32] | (1 << 120%32)。<br /> <strong>b. 减少dns次数。</strong>对于一个站点,使用一次dns解析出IP地址后,下次再遇到该站点的其它网页,就用IP地址替换域名。<br /> <strong>c. 异步连接。</strong>使用单线程非阻塞的方法进行socket连接,充分利用了网络资源和CPU资源。</p>     <p> </p>     <p><span style="font-size:14pt;"><strong>3. larbin的大致流程</strong></span><br />     larbin的大致流程可以从main.cc看出,这里去掉不重要语句,给出关键语句形成的流程,并加上注释。<br /> int main(int argc, char *argv[]) {</p>     <p> global glob(argc, argv)  //使用配置文件初始化global类中的成员变量</p>     <p> for(; ;) {<br />          waitbandwidth()  //如果用户设置了最大带宽,则由此函数保证带宽的使用情况 <br />          input()  //接收用户的输入,得到初始URL列表 <br /> sequencer() //按优先度将URL放到待爬取站点</p>     <p> fetchDns() //对站点名即host,进行DNS请求</p>     <p> fetchOpen() //从DNS解析成功的站点中,取出一些URL进行socket连接 </p>     <p> checkAll() //下载网页,提取URL,并执行用户定制的网页分析<br /> }  <br /> }  </p>     <p> </p>     <p><strong><span style="font-size:14pt;">4. larbin的关键函数</span></strong><br />     这一节主要使用伪代码说明第3节列出的函数是怎样工作的。<br /> // wait to limit bandwidth usage<br /> <strong>waitBandwidth()</strong> {<br />   while( 剩余带宽 < 0 ) {<br />    等10ms<br />    if( socket超时 )  更新待爬取的url数量<br />    更新剩余带宽<br />   }<br /> }</p>     <p>//<br /> <strong>input()</strong> {<br />   初始化webServe,等待用户连接<br />   接收用户输入,包括优先度,深度,抓取模式,初始URL列表<br />   从初始URL得到hostName,portNumber,fileName<br />   按优先度将URL放到待爬取队列<br /> }</p>     <p>//start the sequencer<br /> <strong>sequencer()</strong> {<br />   得到一轮(perCall)可以加载URL的数量(存放在变量still中)<br />   根据URL的优先级加载最多still条URL到待爬取<strong>站点</strong><br /> }</p>     <p>//Opens sockets ; this function perform dns calls, using adns<br /> <strong>fetchDns()</strong> {<br />   从dnsSite取出hostName,发送dns解析请求(发送数量受最大连接数限制)<br />   接收dns解析结果<br />   if(解析成功) {<br />    获取并解析该host的robots.txt<br />    保存URL到okSites<br />   }  <br /> }</p>     <p>//Opens sockets ; Never block (only opens sockets on already known sites) ; work inside the main thread<br /> <strong>fetchOpen()</strong> {<br />   while( 空闲连接数 ) {<br />    从okSites取出一个URL<br />    if( 成功打开socket ) {<br />     向conn填写一些信息  <br />     减少一个空闲连接<br />    }<br />   }<br /> }</p>     <p>//read all data available ; fill fd_set for next select ; give back max fds<br /> <strong>checkAll()</strong> {<br />   for( 每个连接 ) {<br />    switch( 连接状态 ) {<br />     case connecting : 检查是否socket错误,若不是,则将状态转为write,break  <br />     case write : 写socket请求,将状态转为open,break<br />     case open : 读网页,分析网页,提取链接(endInput函数),状态转为empty,break<br />    }<br />   }</p>     <p>  for( 每个连接 ) 更新pollfds数组的状态(与异步IO有关)<br /> }</p>     <p> </p>     <p>a. larbin官网</p>     <p><a href="/misc/goto?guid=4959517980975066304">http://larbin.sourceforge.net/index-eng.html</a></p>     <p>b. larbin的配置和使用<br /> <a href="/misc/goto?guid=4959517981064106243">http://www.cnblogs.com/zhangchaoyang/articles/2031954.html</a></p>     <p>c. Linux网络编程入门<br /> <a href="/misc/goto?guid=4959517981134997612">http://www.cnblogs.com/duzouzhe/archive/2009/06/19/1506699.html</a></p>     <p>d. adns官网<br /> <a href="/misc/goto?guid=4959517981228401112">http://ftp.gnu.org/gnu/adns/</a></p>    </div>