5月の終わり、野口宇宙飛行士が半年近くの国際宇宙ステーションでの長期滞在を終えようとしていた頃のことでした。僕はTwitterで野口宇宙飛行士が宇宙で撮影した写真を仕事帰りに見るのを毎日楽しみにしていたので、あぁ、この美しい写真の数々ももう見れなくなるんだなと寂しく感じていました。
これだけの美しい写真の数々をこのままにしてしまうのはもったいない、とその時思ったので、打上げから帰還までに野口さんが撮影した写真をまとめて見れるようにして、野口さんに感謝のメッセージを伝えよう、と考えました。そこで週末にさっそくTwitpic用のクローラーを作って写真を収集し、それまでの写真をまとめて見れるようにしてから公開したのが、@Astro_Soichi Thank you for your all tweets from space! というページです。
Twitterの僕のつぶやきで公開した当初は、いわゆるTwitter宇宙クラスタのみなさんから反響をいただいて嬉しかったものです。その後は徐々にTwitterを中心に広まっていって、一部の人には宇宙の写真にも興味を持ってもらえたかな?という手応えもあったりして、イチ宇宙開発ファンとしても嬉しかったのを覚えています。
その後しばらくは大きな反響もなかったのですが、今日になって5thstarさん経由で、野口宇宙飛行士自らページを紹介してくれていることを知り、びっくりしました。しかも、野口さん自身も気に入ってくれて、Twitterのプロフィールにも設定してくれているとのことで、2度びっくり!
3ヶ月という時間はかかってしまいましたが、とにかく僕にとっては野口さんに直接的に、少しでも感謝の気持ちが伝わったかなと感じられたのが嬉しくてたまらないです。そして野口さん自身がページを紹介してくれたことで、美しい写真の数々もまた多くの人に見てもらえたであろうことも嬉しく思います。
まったくの余談ですが、野口さんのつぶやきを見た瞬間、何よりも先に「サーバーがやばい!」と思ってしまいました。いわゆる職業病ってやつですね。我ながらなんだかなぁと感じてしまいました (^^;
今週末は今ままで使っていたサーバーのディスクがついにご臨終したので、慌ててデータをサルベージしてサーバーを移転したり、色々セットアップしたりと大変でした。サーバーがようやく安定稼働して、色々楽になったので結果的にはよかったです。さて、今日はnode.jsのモジュール管理について勉強してみます。
前回の標準モジュール編では、node.jsの標準モジュールで遊んでみましたが、標準モジュールの他にも有志が開発した様々なモジュールがGitHubなど配布されています。個々のモジュールをダウンロードしてインストールするのもよいのですが、Perlのcpan、Rubyのgemのようにパッケージ管理ツールがあると便利ですよね。そこで登場したのがnpm (Node Package Manager)です。npmを利用すれば、cpanなどのようにパッケージ名を指定するだけで自由にパッケージをインストールすることができます。
npmを使うには、まずnpm自身をインストールします。npmのサイトにあるスクリプトをダウンロードしてきて実行すればインストール完了です。これでnpmコマンドが自由に使えるようになりました。
$ curl http://npmjs.org/install.sh | sudo sh
$ npm -v
npm it worked if it ends with ok
npm cli [ '-v' ]
0.1.25
つづいてnpmを利用して公開されているモジュールをインストールしてみます。どんなモジュールがあるのかは、npmのリポジトリ一覧や、GitHub上のnode.jsのページにあるモジュール紹介で知ることができます。
さて、今回はブラウザのUserAgent文字列をパースするuser-agent.jsを試しにインストールして使ってみようと思います。以下のようにnpmコマンドからインストールを実行すればインストール完了です。非常に簡単ですね。モジュールが必要なくなったらnpmからアンインストールも実行できます。ちなみにアンインストールするモジュール名にnpmと指定するとnpm自体のアンインストールもすることができます。
// モジュールのインストール
$ sudo npm install user-agent
// モジュールのアンインストール
$ sudo npm uninstall user-agent
// npm自体のアンインストール
$ sudo npm uninstall npm
せっかくなのでインストールしたuser-agent.jsを使ったコードを書いて遊んでみます。以下はブラウザのバージョン名を返すHTTPサーバーのスクリプトです。UserAgent文字列のパースにuser-agent.jsモジュールを利用しています。スクリプトを実行しサーバーを起動してアクセスすると、アクセスしたブラウザ名が表示されるかと思います。
// server.js
var sys = require('sys');
var http = require('http');
var ua = require('user-agent');
// HTTPサーバーを起動
var server = http.createServer(function (req, res) {
// レスポンスとしてブラウザのバージョンを返す
var ua_obj = ua.parse(req.headers['user-agent']);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(ua_obj.fullName);
});
server.listen(8080, "127.0.0.1");
// メッセージを出力
console.log('Server running at http://127.0.0.1:8080/');
$ node server.js
Server running at http://127.0.0.1:8080/
そんなわけで今回はnpmを使ったモジュールの管理について勉強してみました。次回は便利なFrameworkモジュールの使い方あたりを勉強してみたいと思います。
最近、このブログが動いているサーバーの調子が悪くてよく落ちるので、落ち着かない感じの今日この頃です。さて、前回のインストール編に引き続いて、今回は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
[
'http://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
http://blog.summerwind.jp/ - 200
http://twitter.com/ - 200
HTTPリクエストはJSON形式のリストに記載された順に投げられるのですが、非同期に処理されるのでレスポンスが速かったサーバーは先に処理が終了していることがわかります。出力の順番が入れ替わっているのはそのためです。GoogleやYahoo!はさすが速いという感じで、Cookpadもけっこう速いみたいですね。
こんな感じで今回はnode.jsの標準モジュールに触れてみました。次はモジュールの追加や管理というあたりを試してみたいと思います。
Copyright © Moto Ishizawa.