SummerWind

Web, Photography, Space Development

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の標準モジュールに触れてみました。次はモジュールの追加や管理というあたりを試してみたいと思います。

Moto Ishizawa

Moto Ishizawa
ソフトウェアエンジニア。ロケットの打上げを見学するために、たびたびフロリダや種子島にでかけるなど、宇宙開発分野のファンでもある。