初めてのNode.js学習メモ

先日、Webクローラーと機械学習という言葉に惹かれて、「JS+Node.jsによるWebクローラー／ネットエージェント開発テクニック」という書籍を購入しました。
　Node.jsやWebクローラー開発に触れたことがありませんでしたが、書籍を読みつつ、手を動かしてみることで、簡単なWebクローラー作成ができるようになったと思います。ここでは、個人的な学習のまとめを書いてみたいと思います。
　なお、機械学習については、まだ理解が追いついていませんのでここでは記述しません。


# 前提
- Java,JavaScript,HTMLを使ったWebアプリケーションの開発技術については学習経験があります。
- Node.jsは未経験。
- Windows環境を使用。
- Node.js バージョン4.2.6 を使用。


# Node.jsとは
JavaScriptエンジンの一つで、コマンドラインから利用できる。最も普及しているJavaScriptエンジンは、各種ブラウザであるが、セキュリティ面の考慮から、いろいろ制限が多い。Node.jsでは、ファイル処理やネットワーク処理などもできる。さらに、パッケージマネージャー「npm」があり、便利な機能を拡張することが容易。


# Node.jsインストール方法
公式サイトから、MSIファイルをダウンロードして、インストール。
※EXEファイル形式もあったけど、npmが入っていなかった。

Node.js
https://nodejs.org/en/

インストールが終わると、スタートメニューに「Node.js command prompt」が登録されている。これを起動して、次のコマンドを実行できればOK。Windows標準のコマンドプロンプトからも実行できた。

```bat
node -v
```


# パッケージマネージャー npm
npmは、Node.jsで、モジュール（拡張機能）をインストールしたりするためのパッケージマネージャー。インストールしたモジュールの管理場所は、グローバルとローカルの2種類がある。
次のようにコマンドラインから使用できる。

```bat
npm -v
```

主な使い方。

`npm -h` ヘルプを表示。

`npm install モジュール名` モジュールをインストールする。
`npm uninstall モジュール名` モジュールをアンインストールする。
`npm list` インストールされているモジュールの一覧を表示する。
※上記コマンドでは、ローカル環境を対象とする。ローカル環境とはカレントディレクトリのこと。グローバル環境を対象とする場合は、「-g」オプションをつける。

`npm root -g` グローバル環境のパスを表示する。ホームディレクトリ\AppData\Roaming\npm\node_modules となるようです。

私が試した時には、グローバル環境にインストールしたモジュールを使おうとして、「Cannot find module XXX」とエラー表示されることがありました。その場合、次のように環境変数を設定すると、解決しました。

```bat
set NODE_PATH=グローバル環境のパス（npm root -gの結果）
```


# 簡単なサンプル
ハローワールドを出力させる。

ワンライナーで実行。

```bat
node -e "console.log('Hello World');"
```

スクリプトを作成して実行する。

```javascript:helloworld.js
console.log("Hello World");
```

```bat
node helloworld.js
```

Node.jsのリファレンス
[Node.js v4.2.6 Manual & Documentation](https://nodejs.org/dist/latest-v4.x/docs/api/)



# Webページダウンロード（cheerio-httpcliモジュール）
静的なページや、RSS, XMLダウンロードなどに使用可能。逆に、jQuery,Ajaxを多用したようなページでは使用困難な模様。jQuery風に要素を指定できるのが便利。

モジュールインストール

```bat
npm install -g cheerio-httpcli
```

私がインストールしたバージョン
cheerio-httpcli 0.6.4

簡単なスクリプト
Googleで「node.js」を検索して、ページタイトルを標準出力する。

```javascript:cheerio-httpcli-01.js
var client = require('cheerio-httpcli');

client.fetch('http://www.google.com/search',
  { q: 'node.js' },
  function (err, $, res) {
    console.log($('title').text());
});
```

実行

```bat
node cheerio-httpcli-01.js
```

参考ページ
[cheerio-httpcli](https://www.npmjs.com/package/cheerio-httpcli)
[Node.jsのスクレイピングモジュール「cheerio-httpcli」が第3形態に進化したようです - Qiita](http://qiita.com/ktty1220/items/72109a6419e23a26002c)
[Node.jsのスクレイピングモジュール「cheerio-httpcli」が大規模アップデートして帰ってきた - Qiita](http://qiita.com/ktty1220/items/64168e8d416d6d8ffb45)



# Webページダウンロード（PhantomJS,CasperJSモジュール）
PhantomJSが画面表示しないWebブラウザのようなもの、CasperJSがPhantomJSを簡単に使うためのもの、らしい。
cheerio-httpcliでは操作できなかったWebページでも操作できた。コマンドラインから、casperjsコマンドでJavaScriptを実行することになるが、JavaScript内で使うことができるモジュールには制限がある模様。
Pythonのインストールが必要だった。

モジュールインストール

```bat
npm install -g phantomjs
npm install -g casperjs
```

私がインストールしたバージョン
PhantomJS 2.1.1
CasperJS 1.1.0-beta5

簡単なスクリプト
Googleで「node.js」を検索して、ページタイトルを標準出力する。

```javascript:casperjs-01.js
var casper = require("casper").create();

casper.start("http://www.google.com/search?q=node.js", function(){
  this.echo("Title: " + this.getTitle());
});

casper.run();
```

実行

```bat
casperjs casperjs-01.js
```

参考
[CasperJS documentation — CasperJS 1.1.0-DEV documentation](http://casperjs.readthedocs.org/en/latest/index.html)



# メール送信
Yahoo!JAPANメールを使って、メール送信してみる。

モジュールインストール
Yahoo!JAPANメールを使う場合、SMTPサーバを指定してメール送信する。SMTPサーバを使う場合、nodemailer-smtp-transportをインストールしないとダメだった。nodemailerのバージョンによるのかもしれない。

```bat
npm install -g nodemailer
npm install -g nodemailer-smtp-transport
```

私がインストールしたバージョン
nodemailer 2.1.0
nodemailer-smtp-transport 2.2.0

簡単なスクリプト

```javascript:nodemailer01.js
// 実行方法
if (process.argv.length <= 2) {
  console.info("Usage: node nodemailer01 user pass");
  process.exit();
}
var user = process.argv[2];
var pass = process.argv[3];

// SMTPトランスポート作成
var smtpConfig = {
  host: "smtp.mail.yahoo.co.jp",
  port: 465,
  secure: true,
  auth: {
    user: user,
    pass: pass
  },
  // logger: true,
  // debug: true
};
var nodemailer = require("nodemailer");
var smtpTransport = require("nodemailer-smtp-transport");
var transport = nodemailer.createTransport(smtpTransport(smtpConfig));

// メールアドレス
var address = user + "@yahoo.co.jp";

// メール情報の作成
var mailOptions = {
  from: address,
  to: address,
  // from: '"Sender Name" <' + address + '>',
  // to: '"Receiver Name" <' + address + '>',
  subject: "テストメール",
  text: "テストメールです。"
};

// メール送信
transport.sendMail(mailOptions, function(error, response){
  if (error) {
    console.log(error);
    throw error;
  } else {
    console.log("Message sent: " + response.message);
  }
  transport.close();
});
```

参考
[Node.jsでメール送信が出来る – 野村空っぽい](http://nomurakuu.com/softly/224/)



# 補助的な機能
## 実行環境取得

```javascript
console.log("カレントディレクトリ\n" + process.cwd());
console.log("スクリプトパス（絶対パス）\n" + __filename);
console.log("スクリプトディレクトリ\n" + __dirname);
```

```javascript
var path = require("path");
console.log("スクリプトディレクトリ\n" + path.dirname(__filename));
console.log("スクリプト名\n" + path.basename(__filename));
console.log("スクリプト名（拡張子なし）\n" + path.basename(__filename, path.extname(__filename)));
console.log("スクリプト拡張子\n" + path.extname(__filename));
```

```javascript
console.log("実行時引数\n", process.argv);
console.log("実行時引数の数\n", process.argv.length);
console.log("Node.jsコマンド\n" + process.argv[0]);
console.log("スクリプトパス\n" + process.argv[1]);
```

```javascript
var homedir = process.env[process.platform == "win32" ? "USERPROFILE" : "HOME"];
console.log("ホームディレクトリ\n" + homedir);
```

参考
[Web Tips Plus: node.js ホームディレクトリのパスを取得](http://webtipsplus.blogspot.jp/2015/05/nodejs.html)


環境に合った改行コード

```javascript
var os = require("os");
console.log("[" + os.EOL + "]");
```



## メッセージ出力
指定メッセージを出力する。

```javascript
console.log("Hello world.");
console.info("Info log");
console.warn("Warn log");
console.error("Error log");
console.trace("Trace log"); //スタックトレースが出力される。
```

タイムスタンプを付加してメッセージ出力する。

```javascript
var infolog = require("util").log;
infolog("Hello world.");
```

出力/非出力を切り替え可能なメッセージ出力。
出力したい場合は、環境変数NODE_DEBUGに、debuglog関数の引数と同じ文字列を指定しておく。カンマで区切って複数指定することも可能。

```javascript
var debuglog = require("util").debuglog("myapp");
debuglog("デバッグログです。");
```

```bat
set NODE_DEBUG=myapp
```



## 正規表現
簡単に正規表現が使える。あ、これはフツーのJavaScriptの機能だ。

```javascript
console.log("abcde12345".match(/bcd/g)); //-> [ 'bcd' ]
console.log("abcde12345".replace(/bcd/g, "BCD")); //=> aBCDe12345
```

参考
[文字列（String）](http://www.tohoho-web.com/js/string.htm)
[正規表現（RegExp）](http://www.tohoho-web.com/js/regexp.htm)



## 設定ファイル（configモジュール） 

モジュールインストール

```bat
npm install -g config
```

簡単なスクリプト

```javascript:script/nodejs01.js
var config = require("config");
console.log(config.test01);
```

簡単な設定ファイル
JSON5など、いくつかの形式が使える。

```txt:config/default.json5
// default.json5
{
  "test01": "hello world",
}
```

参考
[node.jsのいろいろなモジュール13 – node-configで設定ファイルを切り替えたりする ｜ Developers.IO](http://dev.classmethod.jp/server-side/config/)



## ファイル入出力

```javascript
var fs = require("fs");

fs.writeFileSync("./file-01_out.txt", "ハローワールド", {encoding:"UTF-8"});

var text = fs.readFileSync("./file-01_out.txt", {encoding:"UTF-8"});
console.log(text);
```

参考
[File System Node.js v4.3.1 Manual & Documentation](https://nodejs.org/dist/latest-v4.x/docs/api/fs.html)



## 文字コード
Node.jsでは、内部的にはUTF-16が使用されているらしい。Node.jsは、Shift_JISには対応していないらしいので、Shift_JISを扱うときは、文字コード変換するモジュールが必要。

モジュールインストール

```bat
npm install -g iconv-lite
```

私がインストールしたバージョン
iconv-lite 0.4.13

iconv-liteで対応している日本語文字コードは、UTF-8, CP932, Shift_JIS, EUC-JP。個人的にはこれだけあれば十分だと思う。

```javascript
var iconv = require("iconv-lite");
var fs = require("fs");

fs.writeFileSync(
  "./iconv-lite-01_out.txt",
  iconv.encode("ハローワールド", "Shift_JIS"),
  "binary");

var text = iconv.decode(
  new Buffer(fs.readFileSync("./iconv-lite-01_out.txt", "binary"), "binary"),
  "Shift_JIS");
console.log(text);
```

参考
[GitHub - ashtuchkin/iconv-lite: Convert character encodings in pure javascript.](https://github.com/ashtuchkin/iconv-lite)



## 外部コマンド実行
同期、非同期で実行することが可能。実行する外部コマンドが標準出力などをするときは、文字コードに注意する。

同期的に、dirコマンドを実行してみる。

```javascript
var childProcess = require("child_process");
var iconv = require("iconv-lite");
var buffer = childProcess.execSync("dir",
  {
    timeout: 60 * 1000, // タイムアウト1分
  });
console.log(iconv.decode(buffer, "Shift_JIS"));
```

参考
[Child Process Node.js v4.3.1 Manual & Documentation](https://nodejs.org/dist/latest-v4.x/docs/api/child_process.html)



## 暗号化・復号化
Node.js標準機能のcryptoで、各種アルゴリズムで、暗号化・復号化ができる。

```javascript
var crypto = require("crypto");

if (process.argv.length <= 2) {
  console.log("Usage: node crypto03.js アルゴリズム 暗号化キー 暗号化対象文字列");
  console.log("主要なアルゴリズム");
  console.log("aes128, aes192, aes256 - https://ja.wikipedia.org/wiki/Advanced_Encryption_Standard");
  console.log("des, des3 - https://ja.wikipedia.org/wiki/Data_Encryption_Standard");
  console.log("blowfish - https://ja.wikipedia.org/wiki/Blowfish");
  console.log("アルゴリズム一覧");
  var cipers = crypto.getCiphers();
  console.log(cipers);
  return;
}

// アルゴリズム
var algorithm = process.argv[2];
console.log("アルゴリズム: " + algorithm);

// 暗号化キー
var key = process.argv[3];
console.log("暗号化キー: " + key);

// 暗号化対象文字列
var plainText = process.argv[4];
console.log("暗号化前: " + plainText);

// 暗号化
var cipher = crypto.createCipher(algorithm, key);
var cryptedText = cipher.update(plainText, "utf8", "hex");
cryptedText += cipher.final("hex");
console.log("暗号化後: " + cryptedText);

// 復号化
var decipher = crypto.createDecipher(algorithm, key);
var decryptedText = decipher.update(cryptedText, "hex", "utf8");
decryptedText += decipher.final("utf8");
console.log("復号化後: " + decryptedText);
```



## JSONとJavaScriptオブジェクトの相互変換

```javascript
var obj = {
  "item1": "value1"
};

var jsonText = JSON.stringify(obj);
console.log(jsonText);

var jsonObj = JSON.parse(jsonText);
console.log(jsonObj);
```



## XML/RSS/HTMLの解析
cheerioを使うと、jQuery風に解析出来て楽。

モジュールインストール

```bat
npm install -g cheerio
```

私がインストールしたバージョン
cheerio 0.20.0

簡単なスクリプト

```javascript
var html = ""
  + "<html>"
  + "<body>"
  + "<h1>タイトル</h1>"
  + "</body>"
  + "</html>"
  ;
var cheerio = require("cheerio");
$ = require("cheerio").load(html);
console.log($.html());
console.log($("h1").text());
```

参考
[GitHub - cheeriojs/cheerio: Fast, flexible, and lean implementation of core jQuery designed specifically for the server.](https://github.com/cheeriojs/cheerio)



## CSVデータの読み書き
comma-separated-valuesというモジュールをインストールすると、CSVデータを簡単に扱えるようになる。

私がインストールしたバージョン
comma-separated-values 3.6.4

簡単なスクリプト

```javascript
var data = ""
  + "h1,h2,h3\r\n"
  + "v11,v12,v13\r\n"
  + "v21,v22,v23\r\n"
  + "v31,v32,v33\r\n"
  ;

var CSV = require("comma-separated-values");

// CSVテキストを読み込み、2次元配列を作成する。
var csvArray = new CSV(data).parse();
console.log(csvArray);
// ->出力結果
// [ [ 'h1', 'h2', 'h3' ],
//   [ 'v11', 'v12', 'v13' ],
//   [ 'v21', 'v22', 'v23' ],
//   [ 'v31', 'v32', 'v33' ] ]

// CSVテキストを読み込み、オブジェクト配列を作成する。
var csvObjs = new CSV(data, {header:true}).parse();
console.log(csvObjs);
// ->出力結果
// [ { h1: 'v11', h2: 'v12', h3: 'v13' },
//   { h1: 'v21', h2: 'v22', h3: 'v23' },
//   { h1: 'v31', h2: 'v32', h3: 'v33' } ]

// オブジェクト配列を、CSVテキストに変換する。
var csvText = new CSV(csvObjs).encode();
console.log(csvText);
// ->出力結果
// "v11","v12","v13"
// "v21","v22","v23"
// "v31","v32","v33"
```

参考
[comma-separated-values](https://www.npmjs.com/package/comma-separated-values)

以上で、学習したことを一通り書いてみたはず。