设计网络爬虫

时间:2011-04-29 16:37:46

标签: data-structures search-engine web-crawler google-search large-data-volumes

我遇到了一个采访问题“如果你正在设计一个网络爬虫,你将如何避免陷入无限循环?”我试图回答它。

这一切从一开始就是如何开始的。 比如谷歌开始时,一些中心页面上说有数百个(首先如何找到这些中心页面是一个不同的子问题)。 当谷歌跟踪来自页面的链接等时,它是否继续制作哈希表以确保它不遵循先前访问过的页面。

如果同一页面有两个名称(URL),如果我们有URL缩短器等,那么该怎么办呢。

我以谷歌为例。虽然谷歌没有泄漏其网络爬虫算法和页面排名等的工作方式,但任何猜测?

10 个答案:

答案 0 :(得分:78)

答案 1 :(得分:7)

虽然这里的每个人都已经建议过如何创建您的网络抓取工具,但以下是Google如何对网页进行排名。

Google根据回调链接的数量(其他网站上指向特定网站/页面的链接数量)为每个页面提供排名。这称为相关性得分。这是基于如果一个页面有许多其他页面链接到它的事实,它可能是一个重要的页面。

每个网站/页面都被视为图表中的节点。与其他页面的链接是有向边。顶点的度数被定义为入射边缘的数量。具有较多入射边缘的节点排名较高。

以下是PageRank的确定方式。假设页面Pj具有Lj链接。如果其中一个链接是页面Pi,则Pj将其重要性的1 / Lj传递给Pi。然后,Pi的重要性排名是链接到它的页面所做的所有贡献的总和。因此,如果我们表示通过Bi链接到Pi的页面集合,那么我们有这个公式:

Importance(Pi)= sum( Importance(Pj)/Lj ) for all links from Pi to Bi

将排名放在称为超链接矩阵的矩阵中:H [i,j]

如果存在从Pi到Bi的链接,则此矩阵中的行为0或1 / Lj。该矩阵的另一个特性是,如果我们对列中的所有行求和,则得到1.

现在我们需要将这个矩阵乘以一个名为I(特征值为1)的特征向量,这样:

I = H*I

现在我们开始迭代:我 H,我 H,我 H .... I ^ k * H直到解决方案趋同。也就是说,我们在步骤k和k + 1的矩阵中获得了相同的数字。

现在I矢量中剩下的就是每页的重要性。

对于简单的课堂作业示例,请参阅http://www.math.cornell.edu/~mec/Winter2009/RalucaRemus/Lecture3/lecture3.html

  

至于解决面试问题中的重复问题,请在整个页面上进行校验和,并使用校验和或校验和的bash作为地图中的关键字来跟踪访问过的网页。

答案 2 :(得分:1)

取决于他们的问题有多深。如果他们只是试图避免来回跟踪相同的链接,那么散列URL就足够了。

如果内容中包含数千个导致相同内容的网址?就像QueryString参数一样,它不会影响任何东西,但可以有无限次的迭代次数。我想你也可以对页面的内容进行散列并比较URL,看看它们是否与多个URL标识的catch内容类似。例如,参见@Lirik的帖子中提到的Bot Traps。

答案 3 :(得分:0)

您必须使用某种哈希表来存储结果,您只需在每次加载页面之前检查它。

答案 4 :(得分:0)

这里的问题不是抓取重复的URL,而是使用从url获取的散列通过索引解析。问题是爬行重复内容。 “Crawler Trap”的每个网址都不同(年,日,SessionID ......)。

没有“完美”的解决方案......但您可以使用以下策略:

•保持网站内的网址水平。对于从页面获取URL的每个cicle,增加级别。它就像一棵树。您可以停止在某个级别抓取,例如10(我认为谷歌使用此)。

•您可以尝试创建一种可以比较的HASH来查找类似的文档,因为您无法与数据库中的每个文档进行比较。有来自谷歌的SimHash,但我找不到任何实现可以使用。然后我创造了自己的。我的哈希计数html代码中的低频和高频字符,并生成一个20字节的哈希值,与一个AVLTree内的最后一个爬网页面的小缓存进行比较,其中NearNeighbors搜索具有一定的容差(约为2)。您无法在此哈希中使用对字符位置的任何引用。在“识别”陷阱后,您可以记录重复内容的URL模式,并开始忽略该页面。

•与谷歌一样,您可以为每个网站创建排名,并且比其他网站更“信任”。

答案 5 :(得分:0)

抓取工具会保留一个包含要抓取的所有网址的网址池。为了避免“无限循环”,基本思想是在添加到池之前检查每个URL的存在。

但是,当系统缩放到某个级别时,这并不容易实现。天真的方法是将所有URL保留在哈希集中并检查每个新URL的存在。当有太多的URL无法容纳到内存中时,这将无效。

这里有几个解决方案。例如,我们应该将它们保存在磁盘中,而不是将所有URL存储到内存中。为节省空间,应使用URL哈希而不是原始URL。值得注意的是,我们应该保留URL的规范形式而不是原始形式。因此,如果像bit.ly这样的服务缩短了URL,那么最好获取最终的URL。为了加快检查过程,可以构建缓存层。或者您可以将其视为分布式缓存系统,这是一个单独的主题。

帖子Build a Web Crawler详细分析了这个问题。

答案 6 :(得分:0)

我还需要使用搜寻器,无法找到适合我的需求的爬虫,因此在此之后,我开发了基本的搜寻器库以实现简单的需求。但是能够满足履带的几乎所有原理。 您可以检查DotnetCrawler github repo,该实体使用Entity Framework Core的默认实现自行实现了Downloader-Processor-Pipeline模块,以便将数据保存到Sql Server中。

https://github.com/mehmetozkaya/DotnetCrawler

答案 7 :(得分:0)

网络搜寻器是一种计算机程序,用于从给定的网站URL收集/搜寻以下关键值(HREF链接,图像链接,元数据等)。它的设计非常聪明,可以跟踪从上一个URL已获取的不同HREF链接,因此,Crawler可以从一个网站跳转到另一个网站。通常,它称为Web蜘蛛或Web Bot。这种机制始终是Web搜索引擎的骨干。

请从我的技术博客中找到源代码-http://www.algonuts.info/how-to-built-a-simple-web-crawler-in-php.html

<?php
class webCrawler
{
    public $siteURL;
    public $error;

    function __construct()
    {
        $this->siteURL = "";
        $this->error = "";
    }

    function parser()   
    {
        global $hrefTag,$hrefTagCountStart,$hrefTagCountFinal,$hrefTagLengthStart,$hrefTagLengthFinal,$hrefTagPointer;
        global $imgTag,$imgTagCountStart,$imgTagCountFinal,$imgTagLengthStart,$imgTagLengthFinal,$imgTagPointer;
        global $Url_Extensions,$Document_Extensions,$Image_Extensions,$crawlOptions;

        $dotCount = 0;
        $slashCount = 0;
        $singleSlashCount = 0;
        $doubleSlashCount = 0;
        $parentDirectoryCount = 0;

        $linkBuffer = array();

        if(($url = trim($this->siteURL)) != "")
        {
            $crawlURL = rtrim($url,"/");
            if(($directoryURL = dirname($crawlURL)) == "http:")
            {   $directoryURL = $crawlURL;  }
            $urlParser = preg_split("/\//",$crawlURL);

            //-- Curl Start --
            $curlObject = curl_init($crawlURL);
            curl_setopt_array($curlObject,$crawlOptions);
            $webPageContent = curl_exec($curlObject);
            $errorNumber = curl_errno($curlObject);
            curl_close($curlObject);
            //-- Curl End --

            if($errorNumber == 0)
            {
                $webPageCounter = 0;
                $webPageLength = strlen($webPageContent);
                while($webPageCounter < $webPageLength)
                {
                    $character = $webPageContent[$webPageCounter];
                    if($character == "")
                    {   
                        $webPageCounter++;  
                        continue;
                    }
                    $character = strtolower($character);
                    //-- Href Filter Start --
                    if($hrefTagPointer[$hrefTagLengthStart] == $character)
                    {
                        $hrefTagLengthStart++;
                        if($hrefTagLengthStart == $hrefTagLengthFinal)
                        {
                            $hrefTagCountStart++;
                            if($hrefTagCountStart == $hrefTagCountFinal)
                            {
                                if($hrefURL != "")
                                {
                                    if($parentDirectoryCount >= 1 || $singleSlashCount >= 1 || $doubleSlashCount >= 1)
                                    {
                                        if($doubleSlashCount >= 1)
                                        {   $hrefURL = "http://".$hrefURL;  }
                                        else if($parentDirectoryCount >= 1)
                                        {
                                            $tempData = 0;
                                            $tempString = "";
                                            $tempTotal = count($urlParser) - $parentDirectoryCount;
                                            while($tempData < $tempTotal)
                                            {
                                                $tempString .= $urlParser[$tempData]."/";
                                                $tempData++;
                                            }
                                            $hrefURL = $tempString."".$hrefURL;
                                        }
                                        else if($singleSlashCount >= 1)
                                        {   $hrefURL = $urlParser[0]."/".$urlParser[1]."/".$urlParser[2]."/".$hrefURL;  }
                                    }
                                    $host = "";
                                    $hrefURL = urldecode($hrefURL);
                                    $hrefURL = rtrim($hrefURL,"/");
                                    if(filter_var($hrefURL,FILTER_VALIDATE_URL) == true)
                                    {   
                                        $dump = parse_url($hrefURL);
                                        if(isset($dump["host"]))
                                        {   $host = trim(strtolower($dump["host"]));    }
                                    }
                                    else
                                    {
                                        $hrefURL = $directoryURL."/".$hrefURL;
                                        if(filter_var($hrefURL,FILTER_VALIDATE_URL) == true)
                                        {   
                                            $dump = parse_url($hrefURL);    
                                            if(isset($dump["host"]))
                                            {   $host = trim(strtolower($dump["host"]));    }
                                        }
                                    }
                                    if($host != "")
                                    {
                                        $extension = pathinfo($hrefURL,PATHINFO_EXTENSION);
                                        if($extension != "")
                                        {
                                            $tempBuffer ="";
                                            $extensionlength = strlen($extension);
                                            for($tempData = 0; $tempData < $extensionlength; $tempData++)
                                            {
                                                if($extension[$tempData] != "?")
                                                {   
                                                    $tempBuffer = $tempBuffer.$extension[$tempData];
                                                    continue;
                                                }
                                                else
                                                {
                                                    $extension = trim($tempBuffer);
                                                    break;
                                                }
                                            }
                                            if(in_array($extension,$Url_Extensions))
                                            {   $type = "domain";   }
                                            else if(in_array($extension,$Image_Extensions))
                                            {   $type = "image";    }
                                            else if(in_array($extension,$Document_Extensions))
                                            {   $type = "document"; }
                                            else
                                            {   $type = "unknown";  }
                                        }
                                        else
                                        {   $type = "domain";   }

                                        if($hrefURL != "")
                                        {
                                            if($type == "domain" && !in_array($hrefURL,$this->linkBuffer["domain"]))
                                            {   $this->linkBuffer["domain"][] = $hrefURL;   }
                                            if($type == "image" && !in_array($hrefURL,$this->linkBuffer["image"]))
                                            {   $this->linkBuffer["image"][] = $hrefURL;    }
                                            if($type == "document" && !in_array($hrefURL,$this->linkBuffer["document"]))
                                            {   $this->linkBuffer["document"][] = $hrefURL; }
                                            if($type == "unknown" && !in_array($hrefURL,$this->linkBuffer["unknown"]))
                                            {   $this->linkBuffer["unknown"][] = $hrefURL;  }
                                        }
                                    }
                                }
                                $hrefTagCountStart = 0;
                            }
                            if($hrefTagCountStart == 3)
                            {
                                $hrefURL = "";
                                $dotCount = 0;
                                $slashCount = 0;
                                $singleSlashCount = 0;
                                $doubleSlashCount = 0;
                                $parentDirectoryCount = 0;
                                $webPageCounter++;
                                while($webPageCounter < $webPageLength)
                                {
                                    $character = $webPageContent[$webPageCounter];
                                    if($character == "")
                                    {   
                                        $webPageCounter++;  
                                        continue;
                                    }
                                    if($character == "\"" || $character == "'")
                                    {
                                        $webPageCounter++;
                                        while($webPageCounter < $webPageLength)
                                        {
                                            $character = $webPageContent[$webPageCounter];
                                            if($character == "")
                                            {   
                                                $webPageCounter++;  
                                                continue;
                                            }
                                            if($character == "\"" || $character == "'" || $character == "#")
                                            {   
                                                $webPageCounter--;  
                                                break;  
                                            }
                                            else if($hrefURL != "")
                                            {   $hrefURL .= $character; }
                                            else if($character == "." || $character == "/")
                                            {
                                                if($character == ".")
                                                {
                                                    $dotCount++;
                                                    $slashCount = 0;
                                                }
                                                else if($character == "/")
                                                {
                                                    $slashCount++;
                                                    if($dotCount == 2 && $slashCount == 1)
                                                    $parentDirectoryCount++;
                                                    else if($dotCount == 0 && $slashCount == 1)
                                                    $singleSlashCount++;
                                                    else if($dotCount == 0 && $slashCount == 2)
                                                    $doubleSlashCount++;
                                                    $dotCount = 0;
                                                }
                                            }
                                            else
                                            {   $hrefURL .= $character; }
                                            $webPageCounter++;
                                        }
                                        break;
                                    }
                                    $webPageCounter++;
                                }
                            }
                            $hrefTagLengthStart = 0;
                            $hrefTagLengthFinal = strlen($hrefTag[$hrefTagCountStart]);
                            $hrefTagPointer =& $hrefTag[$hrefTagCountStart];
                        }
                    }
                    else
                    {   $hrefTagLengthStart = 0;    }
                    //-- Href Filter End --
                    //-- Image Filter Start --
                    if($imgTagPointer[$imgTagLengthStart] == $character)
                    {
                        $imgTagLengthStart++;
                        if($imgTagLengthStart == $imgTagLengthFinal)
                        {
                            $imgTagCountStart++;
                            if($imgTagCountStart == $imgTagCountFinal)
                            {
                                if($imgURL != "")
                                {
                                    if($parentDirectoryCount >= 1 || $singleSlashCount >= 1 || $doubleSlashCount >= 1)
                                    {
                                        if($doubleSlashCount >= 1)
                                        {   $imgURL = "http://".$imgURL;    }
                                        else if($parentDirectoryCount >= 1)
                                        {
                                            $tempData = 0;
                                            $tempString = "";
                                            $tempTotal = count($urlParser) - $parentDirectoryCount;
                                            while($tempData < $tempTotal)
                                            {
                                                $tempString .= $urlParser[$tempData]."/";
                                                $tempData++;
                                            }
                                            $imgURL = $tempString."".$imgURL;
                                        }
                                        else if($singleSlashCount >= 1)
                                        {   $imgURL = $urlParser[0]."/".$urlParser[1]."/".$urlParser[2]."/".$imgURL;    }
                                    }
                                    $host = "";
                                    $imgURL = urldecode($imgURL);
                                    $imgURL = rtrim($imgURL,"/");
                                    if(filter_var($imgURL,FILTER_VALIDATE_URL) == true)
                                    {   
                                        $dump = parse_url($imgURL); 
                                        $host = trim(strtolower($dump["host"]));
                                    }
                                    else
                                    {
                                        $imgURL = $directoryURL."/".$imgURL;
                                        if(filter_var($imgURL,FILTER_VALIDATE_URL) == true)
                                        {   
                                            $dump = parse_url($imgURL); 
                                            $host = trim(strtolower($dump["host"]));
                                        }   
                                    }
                                    if($host != "")
                                    {
                                        $extension = pathinfo($imgURL,PATHINFO_EXTENSION);
                                        if($extension != "")
                                        {
                                            $tempBuffer ="";
                                            $extensionlength = strlen($extension);
                                            for($tempData = 0; $tempData < $extensionlength; $tempData++)
                                            {
                                                if($extension[$tempData] != "?")
                                                {   
                                                    $tempBuffer = $tempBuffer.$extension[$tempData];
                                                    continue;
                                                }
                                                else
                                                {
                                                    $extension = trim($tempBuffer);
                                                    break;
                                                }
                                            }
                                            if(in_array($extension,$Url_Extensions))
                                            {   $type = "domain";   }
                                            else if(in_array($extension,$Image_Extensions))
                                            {   $type = "image";    }
                                            else if(in_array($extension,$Document_Extensions))
                                            {   $type = "document"; }
                                            else
                                            {   $type = "unknown";  }
                                        }
                                        else
                                        {   $type = "domain";   }

                                        if($imgURL != "")
                                        {
                                            if($type == "domain" && !in_array($imgURL,$this->linkBuffer["domain"]))
                                            {   $this->linkBuffer["domain"][] = $imgURL;    }
                                            if($type == "image" && !in_array($imgURL,$this->linkBuffer["image"]))
                                            {   $this->linkBuffer["image"][] = $imgURL; }
                                            if($type == "document" && !in_array($imgURL,$this->linkBuffer["document"]))
                                            {   $this->linkBuffer["document"][] = $imgURL;  }
                                            if($type == "unknown" && !in_array($imgURL,$this->linkBuffer["unknown"]))
                                            {   $this->linkBuffer["unknown"][] = $imgURL;   }
                                        }
                                    }
                                }
                                $imgTagCountStart = 0;
                            }
                            if($imgTagCountStart == 3)
                            {
                                $imgURL = "";
                                $dotCount = 0;
                                $slashCount = 0;
                                $singleSlashCount = 0;
                                $doubleSlashCount = 0;
                                $parentDirectoryCount = 0;
                                $webPageCounter++;
                                while($webPageCounter < $webPageLength)
                                {
                                    $character = $webPageContent[$webPageCounter];
                                    if($character == "")
                                    {   
                                        $webPageCounter++;  
                                        continue;
                                    }
                                    if($character == "\"" || $character == "'")
                                    {
                                        $webPageCounter++;
                                        while($webPageCounter < $webPageLength)
                                        {
                                            $character = $webPageContent[$webPageCounter];
                                            if($character == "")
                                            {   
                                                $webPageCounter++;  
                                                continue;
                                            }
                                            if($character == "\"" || $character == "'" || $character == "#")
                                            {   
                                                $webPageCounter--;  
                                                break;  
                                            }
                                            else if($imgURL != "")
                                            {   $imgURL .= $character;  }
                                            else if($character == "." || $character == "/")
                                            {
                                                if($character == ".")
                                                {
                                                    $dotCount++;
                                                    $slashCount = 0;
                                                }
                                                else if($character == "/")
                                                {
                                                    $slashCount++;
                                                    if($dotCount == 2 && $slashCount == 1)
                                                    $parentDirectoryCount++;
                                                    else if($dotCount == 0 && $slashCount == 1)
                                                    $singleSlashCount++;
                                                    else if($dotCount == 0 && $slashCount == 2)
                                                    $doubleSlashCount++;
                                                    $dotCount = 0;
                                                }
                                            }
                                            else
                                            {   $imgURL .= $character;  }
                                            $webPageCounter++;
                                        }
                                        break;
                                    }
                                    $webPageCounter++;
                                }
                            }
                            $imgTagLengthStart = 0;
                            $imgTagLengthFinal = strlen($imgTag[$imgTagCountStart]);
                            $imgTagPointer =& $imgTag[$imgTagCountStart];
                        }
                    }
                    else
                    {   $imgTagLengthStart = 0; }
                    //-- Image Filter End --
                    $webPageCounter++;
                }
            }
            else
            {   $this->error = "Unable to proceed, permission denied";  }
        }
        else
        {   $this->error = "Please enter url";  }

        if($this->error != "")
        {   $this->linkBuffer["error"] = $this->error;  }

        return $this->linkBuffer;
    }   
}
?>

答案 8 :(得分:-1)

网络基本上是一个有向图,因此您可以从网址构建图形,然后在标记受访节点时执行BFS或DFS遍历,这样您就不会两次访问同一页面。

答案 9 :(得分:-2)

这是一个网络抓取工具示例。可用于收集mac欺骗的mac地址。

#!/usr/bin/env python

import sys
import os
import urlparse
import urllib
from bs4 import BeautifulSoup

def mac_addr_str(f_data):
global fptr
global mac_list
word_array = f_data.split(" ")

    for word in word_array:
        if len(word) == 17 and ':' in word[2] and ':' in word[5] and ':' in word[8] and ':' in word[11] and ':' in word[14]:
            if word not in mac_list:
                mac_list.append(word)
                fptr.writelines(word +"\n")
                print word



url = "http://stackoverflow.com/questions/tagged/mac-address"

url_list = [url]
visited = [url]
pwd = os.getcwd();
pwd = pwd + "/internet_mac.txt";

fptr = open(pwd, "a")
mac_list = []

while len(url_list) > 0:
    try:
        htmltext = urllib.urlopen(url_list[0]).read()
    except:
        url_list[0]
    mac_addr_str(htmltext)
    soup = BeautifulSoup(htmltext)
    url_list.pop(0)
    for tag in soup.findAll('a',href=True):
        tag['href'] = urlparse.urljoin(url,tag['href'])
        if url in tag['href'] and tag['href'] not in visited:
            url_list.append(tag['href'])
            visited.append(tag['href'])

更改网址以抓取更多网站......祝你好运