福岡市の公共施設予約状況をスクレイピングする

概要
====
福岡市では，公共施設の予約状況を調べる[ウェブサービス](https://www.comnet-fukuoka.jp/)が提供されている．しかしながら，かなり昔に設計されたサービスで，RESTでもなければ，他のサービスと連携させることもできない仕様になっている．そこで，このサービスをスクレイピングし，各公共施設の予約状況を取得するライブラリ[Fukuoka City Community Space Finder](https://jkawamoto.github.io/fukuoka-city-community-space-finder/)を作成した．

![screen.png](https://qiita-image-store.s3.amazonaws.com/0/62253/ab37e94c-ba85-73c1-0f0d-cd50db57a923.png)


インストールとサンプル
==================
[npm](https://www.npmjs.com/) に登録しているので， `npm` コマンドを使ってインストールできる．

```shell-session
$ npm install fukuoka-city-community-space-finder
```

Fukuoka City Community Space Finder は４つの関数 `area`, `building`, `institution` そして `status` を提供する．初めの３つの関数は公共施設名を検索するためのものである． `area` 関数は福岡市の地域名，要するに区名を返す． `building` は地域名を引数に取り，その地域にある公共施設の一覧を返す． そして，`institution` は地域名，公共施設名を引数に取り，その施設に含まれるレンタル可能な単位の名前，例えば「会議室１」や「テニスコート１」などの一覧を返す．

ちなみに， area, building, institution という名称は元々のサービスで使われていた名称に合わせた．

下記にそれぞれの実行例を示す． なお，スクレイピングは非同期処理のため，各関数とも戻り値は `Promise` である． (参考：[今更だけどPromise入門](http://qiita.com/koki_cheese/items/c559da338a3d307c9d88))

```js
var finder = require("fukuoka-city-community-space-finder");

// 地域名の列挙．
finder.area().then(function(res){
  console.log(res);
});
// -> [ '中央区', '博多区', '東区', '西区', '南区', '城南区', '早良区' ]

// 中央区にある公共施設の列挙．
finder.building("中央区").then(function(res) {
  console.log(res);
});
// -> [ '舞鶴公園', '平和中央公園', '中央体育館', '市民会館',
//      '福岡市文学館（赤煉瓦文化館）', '健康づくりサポートセンター',
//      'ＮＰＯボランティア交流センター', '中央市民センター' ]

// 中央区舞鶴公園にある貸し出し可能な施設の列挙.
finder.institution("中央区", "舞鶴公園").then(function(res) {
  console.log(res);
});
// -> [ '野球場', 'テニスコート１', '球技場' ]
```

最後の例では，「テニスコート１」があるのに「テニスコート２」や「テニスコート３」が無い．しかし，実は「テニスコート２」と「テニスコート３」は存在していて借りることができる．具体的には「テニスコート１」を選ぶと「テニスコート１」から「テニスコート３」が現れるという謎仕様になっている．

`institution` 関数では，連番付きかつ隠されている施設名は取得できない．ただし，次の `state` 関数では，それらの施設に関する情報も取得できる．（スクレイピング元の画面がそうなっているので，我慢してほしい．）

ライブラリが提供する４つめの `state` 関数は，地域名，公共施設名，レンタル単位の名前，予約状況を調べる日付を引数に取り，与えられた日付から向こう一週間の予約状況を返す．

例として，中央区舞鶴公園にある野球場の2016年4月25日以降の予約状況を調べてみる．

```js
var finder = require("fukuoka-city-community-space-finder");

finder.status("中央区", "舞鶴公園", "野球場", 2016, 4, 25).then(
  function(res) {
    console.log(JSON.stringify(res));
  }
);
```

結果は次のようになる．（４月２６日以降は省略）

```json
{
  "04/25(月)": {
    "６００": "closed",
    "９００": "available",
    "１１００": "available",
    "１３００": "available",
    "１５００": "occupied",
    "１７００": "occupied"
  },
  ...
}
```

`600`, `900` などは多分 6:00, 9:00 のことだと思われる．

先ほど述べた「テニスコート１」のように連番施設がある場合，

```js
var finder = require("fukuoka-city-community-space-finder");

finder.status("中央区", "舞鶴公園", "テニスコート１", 2016, 4, 25).then(
  function(res) {
    console.log(JSON.stringify(res));
  }
);
```

実行結果は次のようになる．（４月２６日以降，テニスコート３以降は省略）

```json
{
  "テニスコート１": {
    "04/25(月)": {
      "06:00-19:00": "available"
    },
    ...
  },
  "テニスコート２": {
    "04/25(月)": {
      "06:00-10:00": "available",
      "10:00-12:00": "occupied",
      "12:00-19:00": "available"
    },
    ...
  },
  ...
}
```

なお，各関数の詳細なドキュメントは[Github](https://github.com/jkawamoto/fukuoka-city-community-space-finder#method)を参照のこと．


スタンドアロンでの実行
==================

Node.js 以外からも利用できるようにスタンドアロンでのコマンド `csf` も用意した．グローバル環境にインストールするには，次のコマンドを実行する．(npm コマンドは必要)

```sh
$ npm install -g fukuoka-city-community-space-finder
```

`csf` コマンドの使用方法は次の通りで，各サブコマンドが前節の４つの関数に対応している．

~~~
Usage: csf <command> [options]

Commands:
  area         List up areas.
  building     List up buildings in an area.
  institution  List up institutions in a building
  state        Search reservation status.

Options:
  --help  Show help                                                    [boolean]
~~~

前節の例を `csf` コマンドで行う場合，次のようになる．

```shell-session
# 地域名の列挙．
$ csf area

# 中央区にある公共施設の列挙．
$ csf building --area 中央区

# 中央区舞鶴公園にある貸し出し可能な施設の列挙.
$ csf institution --area 中央区 --building 舞鶴公園

# 中央区舞鶴公園にある野球場の2016年4月25日以降の予約状況
$ csf state --area 中央区 --building 舞鶴公園 --institution 野球場 --year 2016 --month 4 --day 25
```

なお，state サブコマンドで year, month, day を省略した場合，今日の日付が使用される．