7

みなさん、こんにちは!

マルチカール機能を開発してクローラーに実装するには、いくつかの支援が必要です。「スキャンするリンク」の膨大な配列があり、Foreachを使用してループスローします。

ロジックを理解するためにいくつかの擬似コードを使用してみましょう:

    1) While ($links_to_be_scanned > 0).
    2) Foreach ($links_to_be_scanned as $link_to_be_scanned).
    3) Scan_the_link() and run some other functions.
    4) Extract the new links from the xdom.
    5) Push the new links into $links_to_be_scanned.
    5) Push the current link into $links_already_scanned.
    6) Remove the current link from $links_to_be_scanned.

ここで、並列接続の最大数を定義し、リンクごとにこのプロセスを並列に実行できるようにする必要があります。

$links_being_scannedまたはある種のキューを作成する必要があることを理解しています。

正直に言うと、この問題にどのように取り組むかは本当にわかりません。誰かがそれを解決するためのスニペットやアイデアを提供できれば、それは大いにありがたいです。

前もって感謝します!クリス;

拡張:

これはマルチカール自体がトリッキーな部分ではなく、リクエスト後に各リンクで実行される操作の量であることに気づきました。

muticurlの後でも、最終的には、このすべての操作を並行して実行する方法を見つける必要があります。以下で説明するアルゴリズム全体は、並行して実行する必要があります。

ですから、今考え直してみると、次のようなことをしなければなりません。

  While (There's links to be scanned)
  Foreach ($Link_to_scann as $link)
  If (There's less than 10 scanners running)
  Launch_a_new_scanner($link)
  Remove the link from $links_to_be_scanned array
  Push the link into $links_on_queue array
  Endif;

そして、各スキャナーはそうします(これは並行して実行する必要があります):

  Create an object with the given link
  Send a curl request to the given link
  Create a dom and an Xdom with the response body
  Perform other operations over the response body
  Remove the link from the $links_on_queue array
  Push the link into the $links_already_scanned array

スキャナーアルゴリズムを使用して新しいPHPファイルを作成し、並列プロセスごとにpcntl_fork()を使用して、これにアプローチできると思いますか?

マルチカールを使用している場合でも、最終的には他のプロセスの通常のforeach構造でループするのを待つ必要があります。

fsockopenまたはpcntl_forkを使用してこれにアプローチする必要があると思います。

提案、コメント、部分的な解決策、そして「幸運」さえもありがたいです!

どうもありがとう!

4

4 に答える 4

9

免責事項:この回答は、私が関わっているオープンソースプロジェクトにリンクしています。三。あなたは警告されました。

Artax HTTPクライアントはソケットベースのHTTPライブラリであり、(とりわけ)複数の非同期HTTP要求を作成しながら、個々のホストへの同時オープンソケット接続の数をカスタム制御できます。

同時接続数の制限は簡単に実行できます。検討:

<?php

use Artax\Client, Artax\Response;

require dirname(__DIR__) . '/autoload.php';

$client = new Client;

// Defaults to max of 8 concurrent connections per host
$client->setOption('maxConnectionsPerHost', 2);

$requests = array(
    'so-home'    => 'http://stackoverflow.com',
    'so-php'     => 'http://stackoverflow.com/questions/tagged/php',
    'so-python'  => 'http://stackoverflow.com/questions/tagged/python',
    'so-http'    => 'http://stackoverflow.com/questions/tagged/http',
    'so-html'    => 'http://stackoverflow.com/questions/tagged/html',
    'so-css'     => 'http://stackoverflow.com/questions/tagged/css',
    'so-js'      => 'http://stackoverflow.com/questions/tagged/javascript'
);

$onResponse = function($requestKey, Response $r) {
    echo $requestKey, ' :: ', $r->getStatus();
};

$onError = function($requestKey, Exception $e) {
    echo $requestKey, ' :: ', $e->getMessage();
}

$client->requestMulti($requests, $onResponse, $onError);

重要:上記の例では、Client::requestMultiメソッドは指定されたすべてのリクエストを非同期で作成しています。ホストごとの同時実行制限がに設定されているため2、クライアントは最初の2つの要求に対して新しい接続を開き、その後、2つのソケットのいずれかが使用可能になるまで、同じソケットを他の要求に再利用します。

于 2012-10-30T18:05:43.530 に答える
1

あなたはこのようなことを試すことができます、それをチェックしていませんが、あなたはアイデアを得る必要があります

$request_pool = array();

function CreateHandle($url) {
    $handle = curl_init($url);

    // set curl options here

    return $handle;
}

function Process($data) {
    global $request_pool;

    // do something with data

    array_push($request_pool , CreateHandle($some_new_url));
}

function RunMulti() {
    global $request_pool;

    $multi_handle = curl_multi_init();

    $active_request_pool = array();

    $running = 0;
    $active_request_count = 0;
    $active_request_max = 10; // adjust as necessary
    do {
        $waiting_request_count = count($request_pool);
        while(($active_request_count < $active_request_max) && ($waiting_request_count > 0)) {
            $request = array_shift($request_pool);
            curl_multi_add_handle($multi_handle , $request);
            $active_request_pool[(int)$request] = $request;

            $waiting_request_count--;
            $active_request_count++;
        }

        curl_multi_exec($multi_handle , $running);
        curl_multi_select($multi_handle);
        while($info = curl_multi_info_read($multi_handle)) {
            $curl_handle = $info['handle'];
            call_user_func('Process' , curl_multi_getcontent($curl_handle));
            curl_multi_remove_handle($multi_handle , $curl_handle);
            curl_close($curl_handle);
            $active_request_count--;
        }

    } while($active_request_count > 0 || $waiting_request_count > 0);

    curl_multi_close($multi_handle);
}
于 2012-10-30T13:26:14.343 に答える
1

問題に対するより堅牢な解決策を探す必要があります。RabbitMQ は、私が使用した非常に優れたソリューションです。ギアマンもありますが、それはあなたの選択だと思います。

私はRabbitMQが好きです。

于 2012-10-30T13:56:39.900 に答える
0

特定のウェブサイトからメールアドレスを収集するために使用したコードをあなたと共有します。ニーズに合わせて変更できます。そこにある相対URLにいくつかの問題がありました。ここではCURLを使用していません。

<?php
error_reporting(E_ALL);
$home   = 'http://kharkov-reklama.com.ua/jborudovanie/';
$writer = new RWriter('C:\parser_13-09-2012_05.txt');
set_time_limit(0);
ini_set('memory_limit', '512M');

function scan_page($home, $full_url, &$writer) {

    static $done = array();
    $done[] = $full_url;

    // Scan only internal links. Do not scan all the internet!))
    if (strpos($full_url, $home) === false) {
        return false;
    }
    $html = @file_get_contents($full_url);
    if (empty($html) || (strpos($html, '<body') === false && strpos($html, '<BODY') === false)) {
        return false;
    }

    echo $full_url . '<br />';

    preg_match_all('/([A-Za-z0-9_\-]+\.)*[A-Za-z0-9_\-]+@([A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]\.)+[A-Za-z]{2,4}/', $html, $emails);

    if (!empty($emails) && is_array($emails)) {
        foreach ($emails as $email_group) {
            if (is_array($email_group)) {
                foreach ($email_group as $email) {
                    if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
                        $writer->write($email);
                    }
                }
            }
        }
    }

    $regexp = "<a\s[^>]*href=(\"??)([^\" >]*?)\\1[^>]*>(.*)<\/a>";
    preg_match_all("/$regexp/siU", $html, $matches, PREG_SET_ORDER);
    if (is_array($matches)) {
        foreach($matches as $match) {
            if (!empty($match[2]) && is_scalar($match[2])) {
                $url = $match[2];
                if (!filter_var($url, FILTER_VALIDATE_URL)) {
                    $url = $home . $url;
                }
                if (!in_array($url, $done)) {
                    scan_page($home, $url, $writer);
                }
            }
        }
    }
}

class RWriter {
    private $_fh = null;

    private $_written = array();

    public function __construct($fname) {
        $this->_fh = fopen($fname, 'w+');
    }

    public function write($line) {
        if (in_array($line, $this->_written)) {
            return;
        }
        $this->_written[] = $line;
        echo $line . '<br />';
        fwrite($this->_fh, "{$line}\r\n");
    }

    public function __destruct() {
        fclose($this->_fh);
    }
}

scan_page($home, 'http://kharkov-reklama.com.ua/jborudovanie/', $writer);
于 2012-10-30T13:12:52.307 に答える