0

我正在为一个有点慢的网站编写一个页面抓取工具,但有很多信息我想用于小部件目的(在他们的许可下)。目前,执行和解析我到目前为止所抓取4-5 minutes的所有内容都需要大致执行和解析。~150 pages这将是一个crontab'd 事件,并在生成时使用临时表,然后在完成时复制到“实时”表,因此从客户端的角度来看,这是一个无缝过渡,但是您能找到一种加快速度的方法我的代码,可能吗?

//mysql connection stuff here
function dnl2array($domnodelist) {
    $return = array();
    $nb = $domnodelist->length;
    for ($i = 0; $i < $nb; ++$i) {
        $return['pt'][] = utf8_decode(trim($domnodelist->item($i)->nodeValue));
        $return['html'][] = utf8_decode(trim(get_inner_html($domnodelist->item($i))));
    }
    return $return;
}

function get_inner_html( $node ) { 
    $innerHTML= ''; 
    $children = $node->childNodes; 
    foreach ($children as $child) { 
        $innerHTML .= $child->ownerDocument->saveXML( $child ); 
    } 

    return $innerHTML; 
}

// NEW curl instead of file_get_contents()
    $c = curl_init($url);
    curl_setopt($c, CURLOPT_HEADER, false);
    curl_setopt($c, CURLOPT_USERAGENT, getUserAgent());
    curl_setopt($c, CURLOPT_FAILONERROR, true);
    curl_setopt($c, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($c, CURLOPT_AUTOREFERER, true);
    curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($c, CURLOPT_TIMEOUT, 20);

    // Grab the data.
    $html = curl_exec($c);

    // Check if the HTML didn't load right, if it didn't - report an error
    if (!$html) {
        echo "<p>cURL error number: " .curl_errno($c) . " on URL: " . $url ."</p>" .
             "<p>cURL error: " . curl_error($c) . "</p>";
    }

// $html = file_get_contents($url);
$doc = new DOMDocument;

// Load the html into our object
$doc->loadHTML($html);

$xPath = new DOMXPath( $doc );

// scrape initial page that contains list of everything I want to scrape
$results = $xPath->query('//div[@id="food-plan-contents"]//td[@class="product-name"]');
$test['itams'] = dnl2array($results);

foreach($test['itams']['html'] as $get_url){
    $prepared_url[] = ""; // The url being scraped, modified slightly to gain access to more information -- not SO applicable data to see
}
$i = 0;
    foreach($prepared_url as $url){

    $c = curl_init($url);
    curl_setopt($c, CURLOPT_HEADER, false);
    curl_setopt($c, CURLOPT_USERAGENT, getUserAgent());
    curl_setopt($c, CURLOPT_FAILONERROR, true);
    curl_setopt($c, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($c, CURLOPT_AUTOREFERER, true);
    curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($c, CURLOPT_TIMEOUT, 20);

    // Grab the data.
    $html = curl_exec($c);

    // Check if the HTML didn't load right, if it didn't - report an error
    if (!$html) {
        echo "<p>cURL error number: " .curl_errno($c) . " on URL: " . $url ."</p>" .
             "<p>cURL error: " . curl_error($c) . "</p>";
    }

// $html = file_get_contents($url);
        $doc = new DOMDocument;
        $doc->loadHTML($html);

        $xPath = new DOMXPath($doc);

        $results = $xPath->query('//h3[@class="product-name"]');
        $arr[$i]['name'] = dnl2array($results);

        $results = $xPath->query('//div[@class="product-specs"]');
        $arr[$i]['desc'] = dnl2array($results);

        $results = $xPath->query('//p[@class="product-image-zoom"]');
        $arr[$i]['img'] = dnl2array($results);

        $results = $xPath->query('//div[@class="groupedTable"]/table/tbody/tr//span[@class="price"]');
        $arr[$i]['price'] = dnl2array($results);
        $arr[$i]['url'] = $url;
        if($i % 5 == 1){
            lazy_loader($arr); //lazy loader adds data to sql database
            unset($arr); // keep memory footprint light (server is wimpy -- but free!)
        }

        $i++;
        usleep(50000); // Don't be bandwith pig
    }
        // Get any stragglers
        if(count($arr) > 0){
            lazy_loader($arr);
            $time = time() + (23 * 60 * 60); // Time + 23 hours for "tomorrow's date"
            $tab_name = "sr_data_items_" . date("m_d_y", $time);
            // and copy table now that script is finished
            mysql_query("CREATE TABLE IF NOT EXISTS `{$tab_name}` LIKE `sr_data_items_skel`");
            mysql_query("INSERT INTO `{$tab_name}` SELECT * FROM `sr_data_items_skel`");
            mysql_query("TRUNCATE TABLE  `sr_data_items_skel`");
        }
4

5 回答 5

6

听起来您主要是在处理缓慢的服务器响应速度。对于这 150 页中的每一页,即使是 2 秒,您也会看到 300 秒 = 5 分钟。加快速度的最佳方法是使用 curl_multi_* 同时运行多个连接。

因此,将 foreach 循环的开始(通过 if !html 检查)替换为:

reset($prepared_url); // set internal pointer to first element
$running = array(); // map from curl reference to url
$finished = false;

$mh = curl_multi_init();


$i = 0;
while(!$finished || !empty($running)){
    // add urls to $mh up to a maximum
    while (count($running) < 15 && !$finished)
    {
        $url = next($prepared_url);
        if ($url === FALSE)
        {
            $finished = true;
            break;
        }

        $c = setupcurl($url);

        curl_multi_add_handle($mh, $c);

        $running[$c] = $url;
    }

    curl_multi_exec($mh, $active);
    $info = curl_multi_info_read($mh);
    if (false === $info) continue; // nothing to report right now

    $c = $info['handle'];
    $url = $running[$c];
    unset($running[$c]);

    $result = $info['result'];
    if ($result != CURLE_OK)
    {
        echo "Curl Error: " . $result . "\n";
        continue;
    }

    $html = curl_multi_getcontent($c);

    $download_time = curl_getinfo($c, CURLINFO_TOTAL_TIME);

    curl_multi_remove_handle($mh, $c);



    // Check if the HTML didn't load right, if it didn't - report an error
    if (!$html) {
        echo "<p>cURL error number: " .curl_errno($c) . " on URL: " . $url ."</p>\n" .
             "<p>cURL error: " . curl_error($c) . "</p>\n";
    }

    curl_close($c);

    <<rest of foreach loop here>>

这将保持 15 次下载同时进行,并在完成时进行处理。

于 2012-04-22T00:37:54.173 回答
3

无论如何——历史也是如此:请看我的评论。

至于缓存:我正在使用dnsmasq进行缓存。

我的设置是使用厨师的食谱,我通过 chef-solo 运行。模板包含我的配置,属性包含我的设置。这很简单。

所以美妙之处在于,这允许我将此服务器放入 DHCP(我们使用 Amazon EC2,此服务通过 DHCP 将所有 IP 分配给虚拟实例),然后我不必对我的应用程序进行任何更改即可使用它们。

我还有另一个食谱要编辑/etc/dhclient.conf

这有帮助吗?让我知道在哪里详细说明。

编辑

只是为了澄清:这不是一个 Ruby 解决方案,我只是使用 chef 进行配置管理(这部分确保服务始终设置相同,等等)。Dnsmasq 本身充当本地 DNS 服务器并保存请求以加快速度。

手动方式如下:

在 Ubuntu 上:

apt-get install dnsmasq

然后编辑/etc/dnsmasq.conf

listen-address=127.0.0.1
cache-size=5000
domain-needed
bogus-priv
log-queries

重新启动服务并验证它是否正在运行 ( ps aux|grep dnsmasq)。

然后将其放入您的/etc/resolv.conf

nameserver 127.0.0.1

测试:

dig @127.0.0.1 stackoverflow.com

执行两次,检查解决时间。第二个应该更快。

享受!;)

于 2012-04-19T17:14:27.880 回答
1

首先要做的是测量从服务器下载文件所花费的时间。使用函数microtime(true)获取调用前后的时间戳

file_get_contents($url);

并减去这些值。在你发现真正的瓶颈在你的代码内部而不是在网络或远程服务器端之后,你才能开始考虑一些优化。

当您说 150 个页面需要 5 分钟来加载和解析时,即每页 2 秒,我的猜测是大部分时间都用于从服务器下载页面。

于 2012-04-19T16:18:33.283 回答
1

您应该考虑使用 cUrl 而不是 file_get_contents() 和 DOMDocument::loadHTMLFile,因为它要快得多。看到这个问题: https ://stackoverflow.com/questions/555523/file-get-contents-vs-curl-what-has-better-performance

于 2012-04-21T20:12:14.010 回答
0

您需要进行基准测试。DNS 不是问题,如果您要废弃 150 个页面,DNS 肯定会在解析器上缓存 4 分钟,您需要解析其余 149 个页面。

尝试使用 wget/curl 计时页面所有传输,您可能会惊讶于它并没有您想象的那么快。

尝试并行请求,用 4 个并行请求击中它们将使您的时间缩短到 1 分钟。

如果您确实发现这是 xpath 问题,请使用 preg_split() 甚至是带有 popen() 的 awk 脚本来获取您的值。

于 2012-04-21T20:15:29.623 回答