66

私は、1 つの引数 (URL) を取り、その URL から解決された DOM を表す html を返す、内部で使用するための Web サービスを計画しています。解決済みとは、Web サービスが最初にその URL でページを取得し、PhantomJS を使用してページを「レンダリング」し、すべての DHTML、AJAX 呼び出しなどが実行された後に結果のソースを返すことを意味します。ただし、リクエストごとにファントムを起動する (私は現在行っています) のは遅すぎますWeb サービスへの最新の呼び出しを提供するために常に利用できる PhantomJS インスタンスのプールが必要です。

以前にこの種の作業が行われたことはありますか? プール マネージャー/http プロキシ サーバーを自分でゼロから作成するよりも、この Web サービスを他のユーザーの作業に基づいて作成したいと考えています。

詳細なコンテキスト: これまでに見た 2 つの同様のプロジェクトと、それぞれを回避した理由を以下にリストしました。代わりに、PhantomJS インスタンスのプールを管理することについてこの質問が発生しました。

jsdom - 私が見たところ、ページ上でスクリプトを実行するための優れた機能を備えていますが、ブラウザの動作を複製しようとはしていないため、汎用の「DOM リゾルバー」として使用すると、.あらゆる種類のエッジ ケース、イベント呼び出しなどを処理するための多くの余分なコーディング。最初に見た例は、ノードを使用してセットアップしたテスト アプリの body タグの onload() 関数を手動で呼び出さなければならないことでした。深いうさぎの穴の始まりのように思えました。

Selenium - 非常に多くの可動部分があるため、長期間存続するブラウザー インスタンスを管理するためのプールのセットアップは、PhantomJS を使用するよりも複雑になります。マクロの記録/スクリプトの利点は必要ありません。Webページを取得してDOMを解決するのと同じくらいパフォーマンスの高いWebサービスが欲しいだけです(または、画像などを無視できるようにすればさらに高速です)。

4

6 に答える 6

62

私は PhantomJs クラウド サービスをセットアップしました。作業実装に約5週間かかりました。

遭遇する最大の問題は、 PhantomJsでのメモリ リークの既知の問題です。これを回避する方法は、50 回の呼び出しごとにインスタンスを循環させることです。

2 番目に大きな問題は、ページごとの処理が CPU とメモリを大量に消費するため、CPU ごとに 4 つほどのインスタンスしか実行できないことです。

3 番目に大きな問題は、PhantomJs がページ終了イベントとリダイレクトに関して非常に奇抜であることです。ページのレンダリングが実際に完了する前に終了したことが通知されます。 これに対処する方法はいくつかありますが、残念ながら「標準」のものはありません。

対処しなければならない 4 番目に大きな問題は、nodejs と phantomjs の間の相互運用性です。ありがたいことに、この問題に対処するための npm パッケージがたくさんあります。

したがって、私が偏見を持っていることは承知していますが (私が提案する解決策を書いたように)、軽い使用であれば無料のPhantomJsCloud.comをチェックすることをお勧めします。

2015 年 1 月の更新:私が遭遇したもう 1 つの (5 つ目?) 大きな問題は、マネージャー/ロード バランサーからの要求/応答の送信方法です。もともと私は PhantomJS の組み込み HTTP サーバーを使用していましたが、特に最大応答サイズに関して、その制限に直面し続けました。最終的に、通信回線としてローカル ファイル システムへの要求/応答を書きました。 * サービスの実装に費やされた合計時間は、おそらく 20 人週の問題に相当し、おそらく 1000 時間の作業です。*参考までに、次のバージョンに向けて完全に書き直しています.... (進行中)

于 2013-10-28T04:30:11.630 に答える
17

非同期 JavaScript ライブラリはNode で動作しqueue、このような場合に非常に便利な機能を備えています。

queue(worker, concurrency)

指定された同時実行数でキュー オブジェクトを作成します。キューに追加されたタスクは並行して処理されます (同時実行制限まで)。すべてのワーカーが進行中の場合、タスクは 1 つが使用可能になるまでキューに入れられます。ワーカーがタスクを完了すると、タスクのコールバックが呼び出されます。

擬似コード:

function getSourceViaPhantomJs(url, callback) {
  var resultingHtml = someMagicPhantomJsStuff(url);
  callback(null, resultingHtml);
}

var q = async.queue(function (task, callback) {
  // delegate to a function that should call callback when it's done
  // with (err, resultingHtml) as parameters
  getSourceViaPhantomJs(task.url, callback);
}, 5); // up to 5 PhantomJS calls at a time

app.get('/some/url', function(req, res) {
  q.push({url: params['url_to_scrape']}, function (err, results) {
    res.end(results);
  });
});

プロジェクトの readme で、ドキュメント全体をqueue確認してください。

于 2012-04-18T05:21:39.980 に答える
14

私の修士論文では、まさにこれを行うライブラリphantomjs-poolを開発しました。これにより、PhantomJS ワーカーにマップされるジョブを提供できます。ライブラリは、ジョブの配布、通信、エラー処理、ログ記録、再起動などを処理します。このライブラリは、100 万を超えるページのクロールに使用されました。

例:

次のコードは、0 から 9 までの数字の Google 検索を実行し、ページのスクリーンショットをgoogleX.pngとして保存します。4 つの Web サイトが並行してクロールされます (4 つのワーカーが作成されるため)。スクリプトは から開始されnode master.jsます。

master.js (Node.js 環境で実行)

var Pool = require('phantomjs-pool').Pool;

var pool = new Pool({ // create a pool
    numWorkers : 4,   // with 4 workers
    jobCallback : jobCallback,
    workerFile : __dirname + '/worker.js', // location of the worker file
    phantomjsBinary : __dirname + '/path/to/phantomjs_binary' // either provide the location of the binary or install phantomjs or phantomjs2 (via npm)
});
pool.start();

function jobCallback(job, worker, index) { // called to create a single job
    if (index < 10) { // index is count up for each job automatically
        job(index, function(err) { // create the job with index as data
            console.log('DONE: ' + index); // log that the job was done
        });
    } else {
        job(null); // no more jobs
    }
}

worker.js (PhantomJS 環境で実行)

var webpage = require('webpage');

module.exports = function(data, done, worker) { // data provided by the master
    var page = webpage.create();

    // search for the given data (which contains the index number) and save a screenshot
    page.open('https://www.google.com/search?q=' + data, function() {
        page.render('google' + data + '.png');
        done(); // signal that the job was executed
    });

};
于 2015-12-02T19:07:51.333 に答える
1

nodejsを使用している場合、selenium-webdriverを使用しないのはなぜですか

  1. いくつかのphantomjsインスタンスをwebdriverとして実行する phantomjs --webdriver=port_number
  2. 各 Phantomjs インスタンスに対して、PhantomInstance を作成します

    function PhantomInstance(port) {
        this.port = port;
    }
    
    PhantomInstance.prototype.getDriver = function() {
        var self = this;
        var driver = new webdriver.Builder()
            .forBrowser('phantomjs')
            .usingServer('http://localhost:'+self.port)
            .build();
        return driver;
    }
    

    それらすべてを 1 つの配列 [phantomInstance1,phantomInstance2] に入れます。

  3. 配列から無料の phantomInstance を取得する dispather.js を作成し、

    var driver = phantomInstance.getDriver();
    
于 2015-11-10T09:13:17.143 に答える
0

nodejsを使用している場合は、https://github.com/sgentle/phantomjs-nodeを使用できます。これにより、任意の数のphantomjsプロセスをメインのNodeJSプロセスに接続できるため、async.jsを使用できるようになります。そしてたくさんのノードグッズ。

于 2012-10-28T21:45:13.873 に答える