如何在找到所有信息之前创建一个重复的函数?

时间:2010-07-18 05:56:46

标签: php loops web-crawler

我想创建一个PHP功能,通过网站的主页,找到主页中的所有链接,浏览它找到的链接,并继续前进,直到所述网站上的所有链接都是最终的。我真的需要建立这样的东西,这样我就可以抓住我的网站网络并为搜索提供“一站式”。

这是我到目前为止所得到的 -

function spider($urltospider, $current_array = array(), $ignore_array = array('')) {
    if(empty($current_array)) {
        // Make the request to the original URL
        $session = curl_init($urltospider);
        curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
        $html = curl_exec($session);
        curl_close($session);
        if($html != '') {
            $dom = new DOMDocument();
            @$dom->loadHTML($html);
            $xpath = new DOMXPath($dom);
            $hrefs = $xpath->evaluate("/html/body//a");
            for($i = 0; $i < $hrefs->length; $i++) {
                $href = $hrefs->item($i);
                $url = $href->getAttribute('href');
                if(!in_array($url, $ignore_array) && !in_array($url, $current_array)) {
                    // Add this URL to the current spider array
                    $current_array[] = $url;
                }
            }               
        } else {
            die('Failed connection to the URL');
        }
    } else {
        // There are already URLs in the current array
        foreach($current_array as $url) {
            // Connect to this URL

            // Find all the links in this URL

            // Go through each URL and get more links
        }
    }
}

唯一的问题是,我似乎无法理解如何继续前进。谁能帮我吗?基本上,此功能将重复,直到找到所有内容。

4 个答案:

答案 0 :(得分:3)

我不是PHP专家,但你似乎过于复杂了。

function spider($urltospider, $current_array = array(), $ignore_array = array('')) {
    if(empty($current_array)) {
        $current_array[] =  $urltospider;
    $cur_crawl = 0;
    while ($cur_crawl < len($current_array)) { //don't use foreach because that can get messed up if you change the array while inside the loop.
        $links_found = crawl($current_array($cur_crawl)); //crawl should return all links found in the given page
        //Now keep adding $links_found to $current_array. Maybe you can check if any of the links found are already in $current_array so you don't crawl them multiple times
        $current_array = array_merge($current_array, $links_found);
        $cur_crawl += 1;
    }
return $current_array;
}

答案 1 :(得分:2)

您要找的单词是recursion。在foreach循环中,您只需再次调用spider,它将进入每个URL的函数并以递归方式执行爬行。

但是有一个相当重要的问题 - 你没有基本情况,除非你最终到达没有链接到其他页面(死胡同)的页面。此函数将永久运行而不会终止。你需要以几种方式约束它。

  1. 使用memoization记住您已经看过的网址的结果,而不是一遍又一遍地请求同一页面。

  2. 限制您访问特定域的网址,即以“http://www.somedomain.com”开头,这样您就不会最终抓住整个互联网。

答案 2 :(得分:2)

您(可能)想要使用的内容称为“递归”。

网页是图表。图的横向有几种算法;最容易理解的是深度优先。

假设您的网站布局如此(递归终止):

* http://example.com/
  * http://example.com/
    * ...
  * http://example.com/post/1/
    * http://example.com/
      * ...
    * http://example.com/about/
      * ...
    * http://example.com/archives/
      * ...
  * http://example.com/post/2/
    * http://example.com/
      * ...
    * http://example.com/about/
      * ...
    * http://example.com/archives/
      * ...
  * http://example.com/post/3/
    * http://example.com/
      * ...
    * http://example.com/about/
      * ...
    * http://example.com/archives/
      * ...
  * http://example.com/about/
    * http://example.com/
      * ...
    * http://example.com/archives/
  * http://example.com/archives/
    * http://example.com/
      * ...
    * http://example.com/about/
      * ...
    * http://example.com/post/1/
      * http://example.com/
        * ...
      * http://example.com/about/
        * ...
      * http://example.com/archives/
        * ...
    * http://example.com/post/2/
      * http://example.com/
        * ...
      * http://example.com/about/
        * ...
      * http://example.com/archives/
        * ...
    * http://example.com/post/3/
      * http://example.com/
        * ...
      * http://example.com/about/
        * ...
      * http://example.com/archives/
        * ...
    * http://example.com/post/4/
      * http://example.com/
        * ...
      * http://example.com/about/
        * ...
      * http://example.com/archives/
        * ...
    * http://example.com/post/5/
      * http://example.com/
        * ...
      * http://example.com/about/
        * ...
      * http://example.com/archives/
        * ...

当您第一次点击http://example.com/时,您有以下链接:

您需要跟踪已访问的网页,以便忽略它们。 (否则,永远需要蜘蛛页面......字面意思。)每次访问页面时都会添加到忽略列表中。现在,忽略列表中唯一的条目是http://example.com/

接下来,过滤掉忽略的链接,将列表缩小为:

然后,您可以在每个链接上再次运行fetcher。您可以通过使用当前URL和忽略列表再次调用您的函数来执行此操作:spider($url, &$ignoredUrls)(我们使用对$ignoredUrls的引用,以便父级spider调用可以看到新忽略的项。)

查看http://example.com/post/1/,我们会看到以下链接:

我们已经查看了http://example.com/。下一个未被忽略的链接是about页面。在about页面中,我们转到档案页面,查看每个帖子。每个帖子都有相同的链接集:

因为我们已经访问了所有这些链接,所以我们返回一个空数组。

返回/archives/,我们将/post/2/链接(/archives/中的第一个未忽略的链接)附加到$foundLinks局部变量,以及返回值使用spider(这是一个空数组)对/post/2/的调用。然后我们继续第二篇文章。

当我们浏览所有帖子时,我们会返回$foundLinks。除/about/链接外,$foundLinks页面会将这些链接添加到自己的/about/。流程返回/post/1/,查看/archives/(现在被忽略)。 /posts/1/蜘蛛现已完成,并返回自己的$foundLinks。最终,原始呼叫会获得所有找到的链接。


此方法适用于完全关闭的小网站。但是,如果你链接到维基百科,那么你将整天都在寻找。您可以通过以下两种方式解决此问题:

  1. 在一定深度后终止蜘蛛(例如10个深度链接)。
  2. 限制网址,例如到某个域或子域(如example.com)。

  3. 以下是spider(未经测试)的快速实施:

    function get_urls($url) {
        // curl/DOM code here
    }
    
    define('SPIDER_MAX_DEPTH', 10);
    
    function spider_internal($url, &$ignoredUrls, $depth = 0) {
        $foundUrls = array($url);
    
        $ignoredUrls[] = $foundUrls;
    
        if($depth >= SPIDER_MAX_DEPTH) {
            return $foundUrls;
        }
    
        $links = get_links($url);
    
        foreach($links as $link) {
            if(array_search($link, $ignoredUrls) !== false) {
                continue;
            }
    
            $foundUrls = array_merge($foundUrls, spider($link, $ignoredUrls, $depth + 1));
        }
    
        return $foundUrls;
    }
    
    function spider($url) {
        $ignoredUrls = array();
    
        return spider_internal($url, $ignoredUrls);
    }
    

答案 3 :(得分:1)

你肯定不想在抓取网页时使用递归。 :)

适用于小型网站,将占用大型网站上的所有可用内存。例如你有足够的内存来为msn.com上的每个链接抓取(并存储字符串引用)吗?可能没有。