node.jsで遊ぶ: 標準モジュール編
最近、このブログが動いているサーバーの調子が悪くてよく落ちるので、落ち着かない感じの今日この頃です。さて、前回のインストール編に引き続いて、今回はnode.jsに標準搭載されているモジュールをいくつか使ったコードを書いて遊んでみたいと思います。
標準搭載されているモジュールを知るには、まずAPIを見てみるのが一番よいです。ここを見ればファイルの処理やHTTPやURL、DNSといった、いくつかの強力なモジュールの機能を知ることができます。なお、特定のモジュールを使う場合は、最初に必ずrequire()で特定のモジュールを呼び出す必要があります。
今回はまずsysモジュールから使ってみることにします。sysモジュールは標準的な出力機能を提供します。改行なしの文字列を出力するprintメソッドや、日付つきログを出力するlogメソッド、PerlのData::DumperやPHPのvar_dump()のようなダンプ機能をもつinspectメソッドといったメソッドたちが実装されています。以下はsysモジュールを使ったコードとその実行例です。
// sys_sample.js
var sys = require('sys');
var obj = {
name: "Tom Hanks",
age: 54,
};
sys.print("Print message.\n");
sys.log('Log message.');
sys.print(sys.inspect(obj)+"\n");
$ node sys_sample.js
Print message.
11 Aug 21:17:14 - Log message.
{ name: 'Tom Hanks', age: 54 }
お次はconsoleです。Firebugを使い慣れている人にはおなじみの名前ですね。sysモジュールよりもより使いやすい出力メソッドを備えています。require()による読み込みが必要なく、いつでも使うことができます。モジュールとはちょっと違う感じですね。サンプルコードと実行例は以下の通りです。
// console_sample.js
var a = 10;
var obj = {
name: "Tom Hanks",
age: 54,
};
console.info("Info message.");
console.log("Log message.")
console.warn("Warning message."); // 標準エラーに出力
console.error("Error message."); // 標準エラーに出力
console.dir(obj);
console.assert(a = 10); // aが10でなければExceptionが発生
$ node sample.js
Info message.
Log message.
Warning message.
Error message.
{ name: 'Tom Hanks', age: 54 }
さて、今度は色々なモジュールを使ってコードを書いてみます。今回はJSON形式のURLリストをファイルから読み込んで各URLを非同期でクロールし、URLとステータスコードを出力する、ごくシンプルなクローラーを書いてみることにします。コードは以下のような感じになりました。ここでは各処理については解説しませんが、興味があったらコメントを参考に読んでみてください。
/* crawler.js */
// 各種モジュールの読み込み
var fs = require('fs');
var sys = require('sys');
var http = require('http');
var url = require('url');
// リストファイルを取得
var list_path = process.argv[2];
// ファイルの読み込み
readFile(list_path);
// ファイル読み込み関数
function readFile(path) {
fs.readFile(path, function (err, data) {
// エラーの場合は終了
if(err) {
console.error("List file is not exists.");
process.exit(1);
}
// JSONファイルを配列化
var list = eval('('+data+')');
// クロールの実行
crawl(list);
});
}
// クロール関数
function crawl(list) {
// 各URLに対してリクエストを投げる
list.forEach(function (url) {
fetch(url);
});
}
// HTML取得関数
function fetch(target_url) {
// URLをパース
var parsed_url = url.parse(target_url);
// HTTPクライアントを生成
var client = http.createClient(80, parsed_url.hostname);
// リクエストを生成して送信
var request = client.request(
'GET',
parsed_url.pathname,
{'host': parsed_url.hostname}
);
request.end();
// レスポンスイベントハンドラ
request.on('response', function (response) {
var body = "";
// データ取得完了ハンドラ
response.on('end', function(){
// URLとステータスを出力
console.log(target_url+" - "+response.statusCode);
})
});
}
上に記載したcrawler.jsを実行するには、下記のようなURLが記載された配列のJSONファイルをまず用意し、crawler.jsの実行時にそれを指定してあげます。
// list.json
[
'https://blog.summerwind.jp/',
'http://www.apple.com/',
'http://twitter.com/',
'http://nodejs.org/',
'http://www.google.co.jp/',
'http://www.livedoor.com/',
'http://www.yahoo.co.jp/',
'http://cookpad.com/',
]
$ node crawler.js list.json
http://www.yahoo.co.jp/ - 200
http://www.google.co.jp/ - 200
http://cookpad.com/ - 200
http://www.livedoor.com/ - 200
http://www.apple.com/ - 200
http://nodejs.org/ - 200
https://blog.summerwind.jp/ - 200
http://twitter.com/ - 200
HTTPリクエストはJSON形式のリストに記載された順に投げられるのですが、非同期に処理されるのでレスポンスが速かったサーバーは先に処理が終了していることがわかります。出力の順番が入れ替わっているのはそのためです。GoogleやYahoo!はさすが速いという感じで、Cookpadもけっこう速いみたいですね。
こんな感じで今回はnode.jsの標準モジュールに触れてみました。次はモジュールの追加や管理というあたりを試してみたいと思います。