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

Posted: , Modified:   Nodejs Phantomjs Fukuoka OpenData Qiita

本稿は Qiita 投稿記事 のバックアップです.

概要

福岡市では,公共施設の予約状況を調べるウェブサービスが提供されている. しかしながら,かなり昔に設計されたサービスで,RESTでもなければ, 他のサービスと連携させることもできない仕様になっている.そこで,このサービスをスクレイピングし, 各公共施設の予約状況を取得するライブラリ Fukuoka City Community Space Finderを作成した.

インストールとサンプル

npm に登録しているので, npm コマンドを使ってインストールできる.

$ npm install fukuoka-city-community-space-finder

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

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

下記にそれぞれの実行例を示す. なお,スクレイピングは非同期処理のため,各関数とも戻り値は Promise である. (参考:今更だけどPromise入門)

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

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

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

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

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

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

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

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

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

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

結果は次のようになる.(4月26日以降は省略)

{
  "04/25(月)": {
    "600": "closed",
    "900": "available",
    "1100": "available",
    "1300": "available",
    "1500": "occupied",
    "1700": "occupied"
  },
  ...
}

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

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

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

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

実行結果は次のようになる.(4月26日以降,テニスコート3以降は省略)

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

なお,各関数の詳細なドキュメントはGithubを参照のこと.

スタンドアロンでの実行

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

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

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

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 コマンドで行う場合,次のようになる.

# 地域名の列挙.
$ 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 を省略した場合,今日の日付が使用される.