ホームページ >ウェブフロントエンド >jsチュートリアル >NodeJS と PhantomJS を使用して Web サイトのページ情報と Web サイトのスクリーンショットをキャプチャする_JavaScript スキル

NodeJS と PhantomJS を使用して Web サイトのページ情報と Web サイトのスクリーンショットをキャプチャする_JavaScript スキル

WBOY
WBOYオリジナル
2016-05-16 17:14:351194ブラウズ

PhantomJS を使用して Web ページのスクリーンショットを作成するのは経済的で実用的ですが、API が小さいため、他の機能の実行が難しくなります。たとえば、独自の Web サーバー Mongoose は同時に最大 10 個のリクエストしかサポートできませんが、独立したサービスになることを期待するのは現実的ではありません。したがって、サービスをサポートするには別の言語が必要であり、それを完成させるために NodeJS が使用されます。

PhantomJS をインストールします

まず、PhantomJS 公式 Web サイトにアクセスしてプラットフォームに対応するバージョンをダウンロードするか、ソースコードをダウンロードして自分でコンパイルします。次に、環境変数に PhantomJS を設定し、

$ phantomjs

と入力します。

応答がある場合は、次のステップに進むことができます。

PhantomJS を使用して簡単なスクリーンショットを撮ります

コードをコピーします コードは次のとおりです:
var webpage = require('webpage') , page = webpage.create(); page.viewportSize = { 幅: 1024, 高さ: 800 }; 0、幅: 1024、高さ: 800 }; page.settings = { javascriptEnabled: false、loadImages: true、userAgent: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (Gecko のような KHTML) PhantomJS/19.0' }; page.open(' http://www.baidu.com', function (status) { var data; if (status === 'fail') { console.log('ページを開くのに失敗しました!'); } else { page.render(' ./snapshot/test.png') } // メモリを解放します page.close() });
ここではウィンドウ サイズを 1024 * 800 に設定します。

コードをコピーします コードは次のとおりです:
page.viewportSize = { width: 1024, height: 800 };
(0, 0) から始まるサイズ 1024 * 800 の画像をインターセプトします:

コードをコピーします コードは次のとおりです以下:
page.clipRect = { 上: 0、左: 0、幅: 1024、高さ: 800 };
JavaScript を無効にし、画像の読み込みを許可し、userAgent を「Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML、Gecko など) PhantomJS/19.0」に変更します:

コードをコピー コードは次のとおりです:
page.settings = { javascriptEnabled: false,loadImages: true, userAgent: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit / 537.31 (KHTML、Gecko など) PhantomJS/19.0'};
次に、page.open を使用してページを開き、最後にスクリーンショットを ./snapshot/test.png に出力します。

コードをコピーします コードは次のとおりです: page.render('./snapshot/test.png') ;

NodeJS と PhantomJS の通信

まず、PhantomJS でどのような通信ができるかを見てみましょう。 コマンドラインパラメータ

例:

phantomjs snapshot.js http://www.baidu.com

コマンド ライン パラメーターは、PhantomJS を開いたときにのみ渡すことができ、プロセスの実行中にできることは何もありません。

標準出力

コードをコピーします コードは次のとおりです:
標準出力では、PhantomJS から NodeJS にデータを出力できますが、NodeJS から PhantomJS にデータを転送することはできません。

ただし、テストでは標準出力がこれらの方法の中で最も速い送信方法であり、大量のデータを送信する場合は考慮する必要があります。

HTTP

PhantomJS は NodeJS サービスに HTTP リクエストを作成し、NodeJS は対応するデータを返します。

この方法は簡単ですが、リクエストは PhantomJS からのみ行うことができます。

Websocket

PhantomJS 1.9.0 が Websocket をサポートしていることは注目に値します。残念ながら、それは hixie-76 Websocket ですが、結局のところ、NodeJS が PhantomJS とアクティブに通信するためのソリューションを提供しています。

テスト中に、PhantomJS がローカル Websocket サービスに接続するのに実際には約 1 秒かかることがわかりました。当面はこの方法を検討しません。

phantomjs-node

复制代码 代码如下:

phantomjs-nodePhantomJS を NodeJS の 1 つのモデルとして使用することに成功しましたが、作成者の理解:

その質問には質問でお答えします。共有メモリ、ソケット、FIFO、または標準入力をサポートしていないプロセスとどのように通信しますか?

そうですね、PhantomJS がサポートしていることが 1 つあります。それは Web ページを開くことです。実際、Web ページを開くのが非常に得意です。そのため、ExpressJS のインスタンスを起動し、サブプロセスで Phantom を開き、socket.io メッセージを alert() 呼び出しに変換する特別な Web ページを指定することで、PhantomJS と通信します。 alert() の通話は Phantom によって応答され、完了です。

通信自体は、James Halliday の素晴らしい dnode ライブラリを介して行われます。幸いなことに、このライブラリは browserify と組み合わせることで十分に機能し、PhantomJS の pidgin Javascript 環境から直接実行できます。

実際に phantomjs-node で使用されるのは、HTTP または Websocket を使用して通過することですが、その依存度に関係なく、私たちは単一のサービスを考えています。

设计图

はじめに
最初のバージョンでは HTTP を使用して実行することを選択しました。

最初にクラスターを利用して简单の進行守护(index.js):

复制代 代码如下:

module.exports = (function () {
"use strict"
var cluster = require('cluster')
, fs = require('fs');

if(!fs.existsSync('./snapshot')) {
fs.mkdirSync('./snapshot');
}

if (cluster.isMaster) {
cluster.fork();

cluster.on('exit', function (worker) {
console.log('Worker' worker.id ' が死亡しました :(');
process.nextTick(function () {
cluster.fork();
});
})
} else {
require('./extract.js');
}
})();

その後、connect做我们の外部API(extract.js)を利用します:

复制代码代码如下:

module.exports = (function () {
"use strict"
var connect = require('connect')
, fs = require('fs')
, spawn = require('child_process').spawn
、 jobMan = require('./lib/jobMan.js')
、 Bridge = require('./lib/bridge.js')
、 pkg = JSON.parse(fs.readFileSync('./package.json'));

var app = connect()
.use(connect.logger('dev'))
.use('/snapshot', connect.static(__dirname '/snapshot', { maxAge: pkg. maxAge }))
.use(connect.bodyParser())
.use('/bridge', Bridge)
.use('/api', function (req, res, next) {
if (req.method !== "POST" || !req.body.campaignId) return next();
if (!req.body.urls || !req.body.urls.length) return jobMan.watch(req.body.campaignId, req, res, next);

var CampaignId = req.body.campaignId
、imagePath = './snapshot/' CampaignId '/'
、url = []
、url
, imagePath;

function _deal(id, url, imagePath) {
// URL リストにプッシュするだけです
urls.push({
id: id,
url:
画像パス: imagePath
});
}

for (var i = req.body.urls.length; i--;) {
url = req.body.urls[i];
imagePath = ImagesPath i '.png';
_deal(i, url, imagePath);
}

jobMan.register(campaignId, urls, req, res, next);
var snapshot = spawn('phantomjs', ['snapshot.js', CampaignId]);
snapshot.stdout.on( 'data', function (data) {
console.log('stdout: ' data);
});
snapshot.stderr.on('data', function (data) {
console.log('stderr: ' data);
});
snapshot.on('close', function (code) {
console.log('snapshot exited with code ' code);
});

})
.use(connect.static(__dirname '/html', { maxAge: pkg.maxAge }))
.listen(pkg.port, function () { console.log('listen : ' http://localhost:' pkg.port); });

})();

ここでは 2 つのモジュールブリッジとジョブマンを参照しています。

ここで、ブリッジは HTTP 通過ビーム、jobMan はオペレーション管理者です。私たちはキャンペーン ID を介してジョブを処理し、ジョブと応答を jobMan 管理に委任します。その後、PhantomJS を起動して処理します。

通讯桥梁の受領またはジョブを返す関連情報,并交给jobMan(bridge.js):

复制代码代码如下:

module.exports = (function () {
"use strict"
var jobMan = require('./jobMan.js')
, fs = require('fs')
、pkg = JSON.parse(fs.readFileSync('./package.json'));

return function (req, res, next) {
if (req.headers.secret !== pkg.secret) return next();
// スナップショット APP は URL 情報を投稿できます
if (req.method === "POST") {
var body = JSON.parse(JSON.stringify(req.body));
jobMan.fire(body);
res.end(' ');
// スナップショット APP は抽出する URL を取得できます
} else {
var urls = jobMan.getUrls(req.url.match(/campaignId=([^&]*)(s) |&|$)/)[1]);
res.writeHead(200, {'Content-Type': 'application/json'});
res.statuCode = 200;
res. end(JSON.stringify({ urls: urls }));
}
};

})();

リクエストメソッドが POST の場合、PhantomJS が現在送信しているジョブの関連情報と見なされます。GET の場合は、取得ジョブの情報と見なされます。

jobMan 管理ジョブ、応答を介してクライアントに返されるジョブ情報を送信します(jobMan.js):

复制代码代码如下:

module.exports = (function () {
"use strict"
var fs = require('fs')
, fetch = require('./fetch.js')
, _jobs = {};

function _send(campaignId){
var job = _jobs[campaignId];
if (!job) return;
if (job.waiting) {
job.waiting = false;
clearTimeout(job.timeout);
var completed = (job.urlsNum === job.finishNum)
, data = {
CampaignId: CampaignId,
urls: job.urls,
completed: completed
};
job.urls = [];
var res = job.res;
if (finished) {
_jobs[campaignId] = null;
delete _jobs[campaignId]
}
res.writeHead(200, {'Content-Type': 'application/json'});
res.statuCode = 200;
res.end( JSON.stringify(data));
}
}

function register(campaignId, urls, req, res, next) {
_jobs[campaignId] = {
urlsNum: urls.length,
finishNum: 0,
urls: [],
cacheUrls: urls,
res: null,
待機中: false,
タイムアウト: null
} ;
watch(campaignId, req, res, next);
}

function watch(campaignId, req, res, next) {
_jobs[campaignId].res = res;
// 20 秒のタイムアウト
_jobs[campaignId].timeout = setTimeout(function () {
_send(campaignId);
}, 20000);
}

function fire(opts) {
var CampaignId = opts.campaignId
, job = _jobs[campaignId]
, fetchObj = fetch(opts.html);

if (ジョブ) {
if ( opts.status && fetchObj.title) {
job.urls.push({
id: opts.id,
url: opts.url,
image: opts.image,
title: fetchObj.title,
description: fetchObj.description,
status: opts.status
});
} else {
job.urls.push({
id: opts.id,
url: opts.url,
status: opts.status
});
}

if (!job.waiting) {
job.waiting = true;
setTimeout(function () {
_send(campaignId);
}, 500);
}
job.finishNum ;
} else {
console.log('ジョブが見つかりません!');
}
}

function getUrls(campaignId) {
var job = _jobs[campaignId];
if (job) return job.cacheUrls;
}

return {
register: register,
watch: watch,
fire: fire,
getUrls: getUrls
};

})();

ここで私は fetch html を実行してそのタイトルと説明を取得します,fetch 实现比较简单(fetch.js):

复制代码代码如下:

module.exports = (function () {
"厳密な使用"

return function (html) {
if (!html) return { title: false, description: false };

var title = html.match(/

(.*?)/)
, meta = html.match(/ /g)
、説明;

if (meta) {
for (var i = meta.length; i--;) {
if(meta[i].indexOf('name="description"') > -1 || メタ[i].indexOf('name="説明"') > -1){
説明 = メタ[i].match(/content="(.*?)"/)[1] ;
}
}
}

(タイトル && タイトル[1] !== '') ? (title = title[1]) : (title = 'タイトルなし');
説明 || (説明 = '説明なし');

return {
title: タイトル,
description: description
};
};

})();

最後は PhantomJS が実行するソースコードであり、起動後に HTTP 経由でジョブ情報を取得し、ジョブが完了すると、その中の 1 つの URL が HTTP 経由でブリッジに返されます (snapshot.js):

复制代码代码如下:

var webpage = require('webpage')
  , args = require('system').args
  , fs = require('fs')
  , campaignId = args[1]
  , pkg = JSON.parse(fs.read('./package.json'));

function snapshot(id, url, imagePath) {
  var page = webpage.create()
    , send
    , begin
    , save
    , end;
  page.viewportSize = { width: 1024, height: 800 };
  page.clipRect = { top: 0, left: 0, width: 1024, height: 800 };
  page.settings = {
    javascriptEnabled: false,
    loadImages: true,
    userAgent: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.31 (KHTML, like Gecko) PhantomJS/1.9.0'
  };
  page.open(url, function (status) {
    var data;
    if (status === 'fail') {
      data = [
        'campaignId=',
        campaignId,
        '&url=',
        encodeURIComponent(url),
        '&id=',
        id,
        '&status=',
      ].join('');
      postPage.open('http://localhost:' + pkg.port + '/bridge', 'POST', data, function () {});
    } else {
      page.render(imagePath);
      var html = page.content;
      // callback NodeJS
      data = [
        'campaignId=',
        campaignId,
        '&html=',
        encodeURIComponent(html),
        '&url=',
        encodeURIComponent(url),
        '&image=',
        encodeURIComponent(imagePath),
        '&id=',
        id,
        '&status=',
      ].join('');
      postMan.post(data);
    }
    // release the memory
    page.close();
  });
}

var postMan = {
  postPage: null,
  posting: false,
  datas: [],
  len: 0,
  currentNum: 0,
  init: function (snapshot) {
    var postPage = webpage.create();
    postPage.customHeaders = {
      'secret': pkg.secret
    };
    postPage.open('http://localhost:' + pkg.port + '/bridge?campaignId=' + campaignId, function () {
      var urls = JSON.parse(postPage.plainText).urls
        , url;

      this.len = urls.length;

if (this.len) {
for (var i = this.len; i--;) {
url = urls[i];
snapshot(url.id, url.url , url.imagePath);
}
}
});
this.postPage = postPage;
},
post: function (data) {
this.datas .push(data);
if (!this.posting) {
this.posting = true;
this.fire();
}
},
fire: function () {
if (this.datas.length) {
var data = this.datas.shift()
, that = this;
this.postPage.open('http:// localhost:' pkg.port '/bridge', 'POST', data, function () {
that.fire();
// 子プロセスを強制終了
setTimeout(function () {
if ( this.currentNum === this.len) {
that.postPage.close();
phantom.exit();
}
}, 500);
}) ;
} else {
this.posting = false;
}
}
};
postMan.init(snapshot);

效果

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。