使用curl_multi_getcontent()时如何最小化PHP Spider的内存消耗?

时间:2013-08-07 22:15:29

标签: php curl out-of-memory curl-multi

我希望有人可以帮我解决这个问题。我目前正在用PHP写一个蜘蛛函数,它递归地遍历一个网站(通过它在网站页面上找到的链接)直到预先指定的深度。

到目前为止,我的蜘蛛适用于最多2级深度。我的问题是当深度降低3级或更多时,特别是在较大的网站上。我得到了一个致命的内存错误,我认为这与使用cURL的所有递归多处理有关(并且还因为某些站点上的3个级别可能意味着处理了数千个URL)。

  

致命错误:第105行的C:\ xampp \ htdocs \ crawler.php中允许的内存大小为134217728个字节(尝试分配366030个字节)

我的问题是我可能做错了什么(或者我应该做什么)来减少内存消耗。

以下是代码当前的样子,其中与内存使用相关的重要区域保持不变,更复杂的处理部分替换为伪代码/注释(以使其更易于阅读)。谢谢!

<?php

function crawler( $urlArray, $visitedUrlArray, $depth ){

    /* Recursion check 
       --------------- */
    if( empty( $urlArray) || ( $depth < 1 ) ){
        return;
    }

    /* Set up Multi-Handler 
       -------------------- */
    $multiCURLHandler = curl_multi_init();      
    $curlHandleArray= array();

    foreach( $urlArray as $url ){
        $curlHandleArray[$url] = curl_init();
        curl_setopt( $curlHandleArray[$url], CURLOPT_URL, $url );
        curl_setopt( $curlHandleArray[$url], CURLOPT_HEADER, 0 );
        curl_setopt( $curlHandleArray[$url], CURLOPT_TIMEOUT, 1000 );
        curl_setopt( $curlHandleArray[$url], CURLOPT_RETURNTRANSFER , 1 );  
        curl_multi_add_handle( $multiCURLHandler, $curlHandleArray[$url] );
    }

    /* Run Multi-Exec 
       -------------- */
    $running = null;
    do {
        curl_multi_exec( $multiCURLHandler, $running );
    }
    while ( $running > 0 );


    /* Process URL pages to find links to traverse
       ------------------------------------------- */
    foreach( $curlHandleArrayas $key => $curlHandle ){


        /* Grab content from a handle and close it
           --------------------------------------- */
        $urlContent = curl_multi_getcontent( $curlHandle );
        curl_multi_remove_handle( $multiCURLHandler, $curlHandle );
        curl_close( $curlHandle );          


        /* Place content in a DOMDocument for easy link processing
           ------------------------------------------------------- */
        $domDoc = new DOMDocument( '1.0' );
        $success = @$domDoc -> loadHTML( $urlContent );


        /* The Array to hold all the URLs to pass recursively
           -------------------------------------------------- */    
        $recursionURLsArray = array();


        /* Grab all the links from the DOMDocument and add to new URL array
           ---------------------------------------------------------------- */
        $anchors = $domDoc -> getElementsByTagName( 'a' );
        foreach( $anchors as $element ){
            // ---Clean the link
            // ---Check if the link is in $visited
            //    ---If so, continue;
            //    ---If not, add to $recursionURLsArray and $visitedUrlArray
        }


        /* Call the function recursively with the parsed URLs
           -------------------------------------------------- */
        $visitedUrlArray = crawler( $recursionURLsArray, $visitedUrlArray, $depth - 1 );

    }


    /* Close and unset variables
       ------------------------- */
    curl_multi_close( $multiCURLHandler );
    unset( $multiCURLHandler );
    unset( $curlHandleArray );

    return $visitedUrlArray;
}
?>

1 个答案:

答案 0 :(得分:1)

这是你的问题:

 "I'm currently writing a spider function in PHP that recursively crawls across a website"

不要那样做。您将进入无限循环并导致拒绝服务。你真正的问题是内存不足。您真正的问题是您要删除正在抓取的网站。

真正的网络蜘蛛不会攻击您的网站,并且像您正在做的那样点击每一页繁荣热潮。你这样做的方式更像是攻击,而不是合法的网络浏览器。他们被称为“爬行者”,因为他们“爬行”,因为“走得很慢”。另外,合法的webcrawler会读取robots.txt文件,而不会根据该文件读取不受限制的页面。

你应该这样做:

  1. 读取一页并将链接保存到URL具有UNIQUE约束的数据库中,这样您就不会多次获得相同的链接。此表还应具有状态字段,以显示是否已读取URL。

  2. 从状态字段显示未读的数据库中抓取一个URL。阅读它,将它链接到的URL保存到数据库中。更新数据库上的状态字段以显示其已被读取。

  3. 根据需要重复#2 ..但是按照爬行的速度。

    来自http://en.wikipedia.org/wiki/Web_crawler#Politeness_policy

      

    来自访问日志的轶事证据表明,来自已知抓取工具的访问时间间隔各不相同   在20秒到3-4分钟之间。