爬虫Larbin解析(二)——sequencer()

简介:

分析的函数: void sequencer() 

复制代码
//位置:larbin-2.6.3/src/fetch/sequencer.cc
void
sequencer() { bool testPriority = true; if (space == 0) //unit space = 0 { space = global::inter->putAll(); } int still = space; if (still > maxPerCall) //#define maxPerCall 100 still = maxPerCall; while (still) { if (canGetUrl(&testPriority)) { --space; --still; } else { still = 0; } } }
复制代码

所在的文件

larbin-2.6.3/src/fetch/sequencer.h、larbin-2.6.3/src/fetch/sequencer.cc

  View Code
  View Code

 

一、 对于space = global::inter->putAll();

1. interf在global.cc(位置:/larbin-2.6.3/src/global.cc)中的定义为

inter = new Interval(ramUrls);   //#define ramUrls 100000  (位置:larbin-2.6.3/src/types.h)

批注:区别 inter = new Interval(ramUrls);  和 inter = new Interval[ramUrls];  前一个()内是参数,要传入构造函数的;后一个[]内是开辟数组的个数。

2. 类 Interval定义(位置:/larbin-2.6.3/src/fetch/site.h)

  View Code

批注:类内的函数定义为inline。对内联函数的几点说明:

  • 内联函数避免函数调用的开销。将函数指定为内联函数,(通常)就是将它在程序的每个调用点上“内联地”展开,消除调用函数进行的额外开销(调用前先保存寄存器,并在返回时回复)。内联说明(在函数返回值前加inline)对编译器来说只是一个建议,编译器可以选择忽略。一般内敛函数适用于优化小的、只有几行、经常被调用的函数。大多数编译器不支持递归函数的内敛。
  • 把内联函数放在头文件。以便编译器能够在调用点展开同一个函数(保证编译器可见、所有的定义相同)。
  • 编译器隐式地将在类内定义的成员函数当作为内联函数.

 

二、 对于canGetUrl(&testPriority)

函数定义(位置larbin-2.6.3/src/fetch/sequencer.cc)

复制代码
/* Get the next url
 * here is defined how priorities are handled
 按优先级从各个URL队列
 (比如URLsDisk,URLsDiskWait或URLsPriority,URLsPriorityWait)
 获取url保存到某个NameSite(通过url的hash值)

at "global.cc"
// FIFOs
URLsDisk         = new PersistentFifo(reload, fifoFile);
URLsDiskWait     = new PersistentFifo(reload, fifoFileWait);
URLsPriority     = new SyncFifo<url>;
URLsPriorityWait = new SyncFifo<url>;

 */
static bool canGetUrl (bool *testPriority) 
{
    url *u;
    if (global::readPriorityWait != 0)  // 在global.cc声明定义: uint global::readPriorityWait=0;
    {
        global::readPriorityWait--;
        u = global::URLsPriorityWait->get();
        global::namedSiteList[u->hostHashCode()].putPriorityUrlWait(u);
        return true;
    } 
    else if (*testPriority && (u=global::URLsPriority->tryGet()) != NULL) 
    {
        // We've got one url (priority)
        global::namedSiteList[u->hostHashCode()].putPriorityUrl(u);
        return true;
    } 
    else 
    {
        *testPriority = false;
        // Try to get an ordinary url
        if (global::readWait) 
        {
          global::readWait--;
          u = global::URLsDiskWait->get();
          global::namedSiteList[u->hostHashCode()].putUrlWait(u);
          return true;
        } 
        else 
        {
            u = global::URLsDisk->tryGet();
            if (u != NULL) 
            {
                global::namedSiteList[u->hostHashCode()].putUrl(u);
                return true;
            }
            else 
            {
                return false;
            }
        }
    }
}
复制代码

1. 为什么diskpriority的队列都是成对出现的,是因为可以认为每个sitenamedSiteList当中都有一个小的队列来保存它的url,这个url的个数是有个数限制的,当超过这个限制的时候就不能再把该site下的url放入,但也不能丢弃,而是放入wait队列。Larbin会控制一段时间在disk队列中取url,一段时间在diskWait当中取urldiskpriority的区别只是优先级的区别。namedSiteList的作用是实现了DNS缓存

          

2. global::readPriorityWait 的值由main.cc的cron()函数中变化得知

复制代码
// see if we should read again urls in fifowait
if ((global::now % 300) == 0) {
    global::readPriorityWait = global::URLsPriorityWait->getLength();
    global::readWait = global::URLsDiskWait->getLength();
}
if ((global::now % 300) == 150) {
    global::readPriorityWait = 0;
    global::readWait = 0;
}
复制代码

这里global::now%300是判断这次是对wait里的url进行处理,还是对不是wait里的进行处理,这里的%300等于0150的概率都是1/300,所以大约300次换一次。readPriorityWaitURLsPriorityWait中的长度(也就是url的数量);readWait是URLsDiskWait中url的个数。

3. 在canGetUrl中,在对于每个站点,将相应的url放进去。putPriorityUrlWait, putPriorityUrl, putUrlWait, putUrl在site.h的定义如下

复制代码
/** Put an url in the fifo
 * If there are too much, put it back in UrlsInternal
 * Never fill totally the fifo => call at least with 1 */
void putGenericUrl(url *u, int limit, bool prio);
inline void putUrl(url *u) {
    putGenericUrl(u, 15, false);
}
inline void putUrlWait(url *u) {
    putGenericUrl(u, 10, false);
}
inline void putPriorityUrl(url *u) {
    putGenericUrl(u, 5, true);
}
inline void putPriorityUrlWait(url *u) {
    putGenericUrl(u, 1, true);
}
复制代码

 可以发现,每次都是调用函数putGenericUrl,其定义如下

复制代码
/* Put an url in the fifo if their are not too many */
void NamedSite::putGenericUrl(url *u, int limit, bool prio) 
{
if (nburls > maxUrlsBySite - limit)
{
// Already enough Urls in memory for this Site // first check if it can already be forgotten if (!strcmp(name, u->getHost()))
{
if (dnsState == errorDns)
{ nburls
++; forgetUrl(u, noDNS); return; } if (dnsState == noConnDns)
{ nburls
++; forgetUrl(u, noConnection); return; } if (u->getPort() == port && dnsState == doneDns && !testRobots(u->getFile()))
{ nburls
++; forgetUrl(u, forbiddenRobots); return; } } // else put it back in URLsDisk refUrl(); global::inter->getOne(); if (prio)
{
global::URLsPriorityWait->put(u); }
else
{
global::URLsDiskWait->put(u); } }
复制代码

如果已经有足够多的url在内存里,执行这里if中的代码,strcmp(name,u->getHost())是判断这个主机是不是已经就进行过dns方面的判断,也就是说对于一个站点,只做一次dns解析的判断,以后就按这个结果进行处理,dnsStatenoDnsnoConnDns,还有robots.txt不允许的情况,如果没有问题,就把它放到URLsDisk中。

复制代码
else {
    nburls++;
    if (dnsState == waitDns || strcmp(name, u->getHost()) || port
           != u->getPort() || global::now > dnsTimeout) {
       // dns not done or other site
       putInFifo(u);
       addNamedUrl();
       // Put Site in fifo if not yet in
       if (!isInFifo) {
           isInFifo = true;
           global::dnsSites->put(this);
       }
    } else
       switch (dnsState) {
       case doneDns:
           transfer(u);
           break;
       case errorDns:
           forgetUrl(u, noDNS);
           break;
       default: // noConnDns
           forgetUrl(u, noConnection);
       }
}
复制代码

 如果需要判断dns能不能解析,就将它放到dnsSites里,这个会在fetchDns中判断。或是如果还能放到内存里,并且又是doneDns,表示可以解析,就调用transfer

复制代码
void NamedSite::transfer(url *u) {
    if (testRobots(u->getFile())) {
       if (global::proxyAddr == NULL) {
           memcpy(&u->addr, &addr, sizeof(struct in_addr));
       }
       global::IPSiteList[ipHash].putUrl(u);
    } else {
       forgetUrl(u, forbiddenRobots);
    }
}
复制代码

这里是将url放入到IPSiteList的相应ipHash中。

 

附类的定义

类url定义(larbin-2.6.3/src/utils/url.h  larbin-2.6.3/src/utils/url.cc

  View Code
  View Code

global::namedSiteList

NamedSite *global::namedSiteList;
namedSiteList = new NamedSite[namedSiteListSize];
  View Code
  View Code

 

其中两个类的定义

larbin-2.6.3/src/utils/PersistentFifo.h、larbin-2.6.3/src/utils/PersistentFifo.cc

  View Code
  View Code

Larbin-2.6.3/src/utils/syncFifo.h

  View Code

 





本文转自jihite博客园博客,原文链接:http://www.cnblogs.com/kaituorensheng/p/3789621.html,如需转载请自行联系原作者

相关文章
|
4月前
|
数据采集 开发者 Python
Python爬虫实战:利用Beautiful Soup解析网页数据
在网络爬虫的开发过程中,数据解析是至关重要的一环。本文将介绍如何利用Python的Beautiful Soup库来解析网页数据,包括解析HTML结构、提取目标信息和处理特殊情况,帮助开发者更好地实现爬虫功能。
|
2月前
|
数据采集 存储 JSON
解析Perl爬虫代码:使用WWW::Mechanize::PhantomJS库爬取stackoverflow.com的详细步骤
在这篇文章中,我们将探讨如何使用Perl语言和WWW::Mechanize::PhantomJS库来爬取网站数据。我们的目标是爬取stackoverflow.com的内容,同时使用爬虫代理来和多线程技术以提高爬取效率,并将数据存储到本地。
|
2月前
|
数据采集 前端开发 JavaScript
Java网络爬虫实践:解析微信公众号页面的技巧
Java网络爬虫实践:解析微信公众号页面的技巧
|
2月前
|
数据采集 存储 监控
Python爬虫实战:利用BeautifulSoup解析网页数据
在网络信息爆炸的时代,如何快速高效地获取所需数据成为许多开发者关注的焦点。本文将介绍如何使用Python中的BeautifulSoup库来解析网页数据,帮助你轻松实现数据抓取与处理的技术。
|
3月前
|
数据采集 XML 数据处理
Python爬虫实战:利用BeautifulSoup解析网页数据
本文将介绍如何利用Python中的BeautifulSoup库来解析网页数据,帮助读者更好地开发爬虫程序,实现自动化数据采集与处理。
|
3月前
|
数据采集 文字识别 Linux
Python爬虫解析库安装#1
摘要:python解析库 lxml安装、beautiful soup 安装、pyquery安装、tesserocr安装【2月更文挑战第3天】
61 3
Python爬虫解析库安装#1
|
5月前
|
数据采集 XML 数据格式
python爬虫入门篇:如何解析爬取到的网页数据?试下最简单的BeautifulSoup库!
前面笔记解析了如何使用requests模块向网站发送http请求,获取到网页的HTML数据。这篇我们来如何使用BeautifulSoup模块来从HTML文本中提取我们想要的数据。Beautiful Soup,简称bs4,是Python的一个HTML或XML的解析库,一般用它来从网页中提取数据。
58 1
|
5月前
|
数据采集 数据安全/隐私保护 Python
深度解析Python爬虫中的隧道HTTP技术
深度解析Python爬虫中的隧道HTTP技术
|
1天前
|
分布式计算 Java API
Java8 Lambda实现源码解析
Java8的lambda应该大家都比较熟悉了,本文主要从源码层面探讨一下lambda的设计和实现。
|
1天前
|
算法 Java Go
ArrayList源码解析
ArrayList源码解析
7 1

推荐镜像

更多