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を実行するchefのレシピを使用しています。テンプレートには私の構成が含まれ、属性には私の設定が含まれています。それはかなり簡単です。

つまり、これにより、このサーバーをDHCPに配置でき(Amazon EC2を使用し、このサービスはDHCPを介してすべてのIPを仮想インスタンスに配布します)、アプリケーションを変更してそれらを使用する必要がないのがすばらしい点です。

編集する別のレシピがあります/etc/dhclient.conf

これは役に立ちますか?詳細を教えてください。

編集

明確にするために:これは、構成管理にchefを使用しているRubyソリューションではありません(この部分では、サービスが常に同じようにセットアップされていることなどを確認します)。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

2回実行し、解決にかかった時間を確認します。2つ目はもっと速いはずです。

楽しみ!;)

于 2012-04-19T17:14:27.880 に答える
1

file_get_contents() と DOMDocument::loadHTMLFile の両方の代わりに cUrl を使用することを検討する必要があります。この質問を参照してください: https://stackoverflow.com/questions/555523/file-get-contents-vs-curl-what-has-better-performance

于 2012-04-21T20:12:14.010 に答える
1

最初に行うことは、サーバーからファイルをダウンロードするのにかかる時間を測定することです。関数microtime(true)を使用して、呼び出しの前後のタイムスタンプを取得します

file_get_contents($url);

値を減算します。本当のボトルネックがネットワークやリモート サーバー側ではなく、コード内にあることがわかった後で、最適化について考え始めることができます。

150 ページの読み込みと解析に 5 分かかると言うと、それは 1 ページあたり 2 秒であり、私の推測では、その時間のほとんどはサーバーからページをダウンロードするために費やされます。

于 2012-04-19T16:18:33.283 に答える
0

ベンチマークする必要があります。DNS は問題ではありません。150 ページを破棄する場合、残りの 149 ページを解析するのに必要な 4 分間、DNS は確実にリゾルバーにキャッシュされます。

wget/curl を使用してページのすべての転送のタイミングを計ってみてください。思ったほど速くないことに驚くかもしれません。

並行してリクエストしてみてください。4 つの並行リクエストでヒットすると、時間が 1 分に短縮されます。

実際に xpath の問題であることがわかった場合は、 preg_split() または awk スクリプトと popen() を使用して値を取得してください。

于 2012-04-21T20:15:29.623 に答える