第29回 鉄道運行情報をWebから取得して解析する

今回は鉄道運行情報をWebから取得して解析、LEDの点滅パターンに変換してみます。

今回の説明内容

前回は天気予報データの文字列からLEDの点滅パターンに変換しました。今回は鉄道運行情報をWebから取得して解析し、さらにLEDの点滅パターンに変換してみます。

今回の説明内容は以下になります。

  • Webスクレイピング
  • 鉄道運行情報データ解析の方針
  • PHPで鉄道運行情報文字列解析プログラムを作成
  • Pythonで鉄道運行情報文字列解析プログラムを作成

 

Webスクレイピング

前回の天気予報データは、「Weather Hacks」というサイトから取得しました。ここは、個人利用であれば天気予報データをWebAPIで取得して自由に使っていいですよ、ってサイトです。

では、鉄道運行情報も同じようなサイトがあるかというと、、、なかなか気軽に使えそうなところは見つかりませんでした。WebAPIで鉄道運行情報を提供しているサイトはありますが、有償だったり、カバーしている路線が限定されていたり、という感じです。

そこで、鉄道運行情報はYahoo! Japanの運行情報サイトからデータを取得して、解析、LEDの点滅パターンに変換してみます。

このように通常のWebページをプログラムで取得して利用する行為を「Webスクレイピング」や「Webクローリング」、「Webスパイダリング」などと呼ばれています。このWebスクレイピングを行う場合、十分な注意が必要です。

通常のWebページは人が見ることを想定しています。人が見る場合、そのページにアクセスする頻度や回数は限界がありますよね。ところが、プログラムでWebページを取得する場合、高頻度で大量のデータ取得もやろうと思えばできてしまいます。例えば刻々と変わる株価データを手動で取得するのは大変なので、プログラムで株価データを高頻度で大量に取得する、ということも考えられます。でも多くの人がそのようなことをすると、そのWebサイトにはかなりの負荷がかかってしまい、品質の高いデータ提供ができなくなってしまいます。

そこで、サイトによってはWebスクレイピングを禁止しているところがあります。例えば、Yahoo! ファイナンスのページでは、以下のように株価などの情報を取得する目的のWebスクレイピングを禁止しています。

YahooFinance web scraping

Yahoo! Japanの運行情報サイトは特にWebスクレイピングは禁止されていないようです。ただ禁止されていないからといって、不必要にデータを大量に取得すればサーバに負荷がかかりますので、データ取得は最低限にしましょう。また取得したデータの2次利用する場合も著作権の観点で注意が必要です。

なお、Webスクレイピングについては、以下のサイトに詳しい解説がありますので参考にしてみてください。

Webスクレイピングのノウハウを公開します (東北ギーク)

今回の鉄道運行情報サイトのトップページの “robots.txt” にもスクレイピングに関する記述はありませんでした。また運行情報表示ページのHTMLコードにもrobotsに関する記述はありませんでした。さらに実際の取得は、1日に数回程度の取得で個人利用目的ですので、特に問題ないと判断しました。

 

鉄道運行情報データ解析の方針

早速Yahoo! Japanの運行情報サイトのデータを確認してみましょう。

確認に入る前に、何度もサイトからデータを取得することがないように、一度HTMLデータを取得しておきます。取得したデータを使って解析方法の検討や実際の動作確認をすることにします。取得したデータでのどうさかくにんができたら実際にサイトからデータを取得→解析の確認をします。

サイトのHTMLデータを取得するには wget コマンドを使用します。使い方は、wgetの後に取得したいURLを記述すればOKです。

それでは、最初にURLを調べておきます。まずYahoo! Japanの運行情報トップページに行きます。

Yahoo! Japan路線情報 – 運行情報トップページ

次に情報取得が必要な路線のエリアをクリックします。

Railway status top

指定したエリアの運行情報ページが表示されたら、URLをコピーします。

Railway status area

次にRaspberry Piにssh接続して、wgetの後にURLをペーストして実行します。以下は関東エリアのURLを使用しています。

pi@raspberrypi ~/dev $ wget http://transit.yahoo.co.jp/traininfo/area/4/
--2016-01-10 14:35:05--  http://transit.yahoo.co.jp/traininfo/area/4/
transit.yahoo.co.jp (transit.yahoo.co.jp) をDNSに問いあわせています... 182.22.11.123
transit.yahoo.co.jp (transit.yahoo.co.jp)|182.22.11.123|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 特定できません 
`index.html' に保存中

index.html                        [ <=>                                                ]  49.86K  --.-KB/s 時間 0.03s  

2016-01-10 14:35:05 (1.74 MB/s) - `index.html' へ保存終了 [51054]

このような感じで表示されましたでしょうか。表示メッセージの中に、「 `index.html`に保存中」とあります通り、データは同じディレクトリに index.htmlというファイル名で保存されています。それでは、このindex.htmlの中身を見てどのように運行状況データを取得するか考えてみます。例として取得する対象は山手線にします。

山手線の状況はここのデータを取得すれば解析できますね。

Yamate line 1

これに対応するHTMLデータを確認すると、以下のようになっています。

<tr>
<td><a href="http://transit.yahoo.co.jp/traininfo/detail/21/0/">山手線</a></td>
<td>平常運転</td>
<td>事故・遅延情報はありません</td>
</tr>

なお、状況が「平常運転」以外の場合は、ページの上の方の「現在運行情報のある路線」というところにも同じ内容が表示されます。確認したところ、ここに表示される内容に対応するHTMLコードは、下の方のリストのHTMLコードと同じでした。ということで、上のデータ解析ができればOKそうです。

運行状況文字列の取得はこのようにしたいと思います。まず該当する路線名の行を探します。この例では「山手線」という文字列の行を探します。その行が見つかったら、次の行に運行情報の文字列がありますので、次の行の文字列判定を行う、という方法です。路線名の検索ですが、他の領域にたまたま「山手線」という言葉がある場合は解析できないかもしれませんので、「山手線」だけではなく念のため「山手線</a></td>」を検索するようにしたいと思います。

次に運行情報の文字列ですが、全て確認できていませんが、「平常運転」「列車遅延」「交通障害情報」「その他」があるようです。運行停止の文字列もあるのですが確認できていません。運行情報文字列からLEDの点滅パターンに変換する方針を検討します。

まず平常運転時は緑色で点灯、遅延の場合は黄色で点滅、それ以外は赤色で点滅にしてみます。平常運転と遅延以外の状況はLEDの点滅パターンで表現するのは難しいですので、このようにしてみました。

プログラムの概要としては、以下になります。

  1. 運行情報ページのHTMLデータを取得する。ただし検討の段階ではWebから取得するのではなく、一度取得したローカルに保存してあるHTMLデータを読み込む
  2. 読み込んだHTMLデータを1行ずつ分解して、各行のHTML文字列を配列に格納する
  3. すべての配列要素から「山手線</a></td>」を検索する
  4. 見つかったら次の行の運行情報文字列の一致を確認する
  5. 「平常」という文字があれば緑色点灯、「遅延」という文字があれば黄色点滅、それ以外は赤色点滅のパターンにする

これで運行情報からLED点滅パターンに変換できそうですので、この処理をプログラムにしてみます。なお、LEDの点滅パターンは配列に入れますが、入れ方などは天気予報データと同様にしてあります。

 

PHPで鉄道運行情報文字列解析プログラムを作成

以下はローカルに保存してある運行情報ページのHTMLデータを読み込んで、LEDパターンに変換するプログラムです。必要と思われる箇所については後に説明しています。

#!/usr/bin/php

<?php

// 情報取得する路線名設定
$line_name = "山手線";

// Yahoo! Japan運行情報のURL
// 以下は関東地方の運行情報URL
$url = "http://transit.yahoo.co.jp/traininfo/area/4/";

// Yahoo! Japan運行情報ページのHTMLデータ取得
// ただしテスト用なのでWebからの取得はせずにローカルに保存してあるファイルを読み込む
// $railway_html = file_get_contents($url);
$railway_html = file_get_contents("./index.html");

// 取得したHTMLデータを改行で分割して配列に格納
$railway_info = explode("\n", $railway_html);

// LED点滅パターン
$led_pat = [];

// 配列に入れたHTMLデータ各行を解析
for($i=0; $i<count($railway_info)-1; $i++) {
    // 情報取得する路線名文字列が含まれているかチェック
    $pos = strpos($railway_info[$i], $line_name."</a></td>");
    if ($pos !== false) {
        // 含まれていれば次の行に運行情報文字列があるので
        // 運行情報に合わせたLEDの色とパターンを格納
        // まず「平常」が含まれるかチェック
        $pos = strpos($railway_info[$i+1], "平常");
        if($pos !== false) {
            // 平常運転であればLED色は緑で点灯
            $led_pat += [ "rail_color" => "green" ];
            $led_pat += [ "rail_pat" => ["on", "on", "on"] ];
        } else {
            // 平常運転でなければ「遅延」が含まれるかチェック
            $pos = strpos($railway_info[$i+1], "遅延");
            if($pos !== false) {
                // 遅延であればLED色は黄色で点滅
                $led_pat += [ "rail_color" => "yellow" ];
                $led_pat += [ "rail_pat" => ["blink", "blink", "blink"] ];
            } else {
                // 平常運転、遅延でもなければLEDを赤点滅して警告する
                $led_pat += [ "rail_color" => "red" ];
                $led_pat += [ "rail_pat" => ["blink", "blink", "blink"] ];
            }
        }
    }
}

echo $line_name . "の運行状況LED点滅パターン\n";
echo "LED色: " . $led_pat["rail_color"] . "\n";
echo "LED点滅パターン:\n";
var_dump($led_pat["rail_pat"]);

?>

6行目:

$line_name変数に、情報取得する路線名を代入しておきます。

14行目:

この行は実際にYahoo! Japanの運行情報Webサイトからデータを取得する行ですが、検討段階で何度も取得しないようにコメントアウトしています。

15行目:

その代わりにこの行でローカルに保存してあるHTMLデータを読み込みます。

18行目:

HTMLデータの各行を分解して配列に保存します。PHPではexplode関数で可能です。explode(区切り文字, 元データ)と指定すると、元データを区切り文字で分解して、分解した要素を一つづす配列にします。返り値はその配列になります。

24行目:

このfor文で、配列に入れたHTMLデータの各行を調べていきます。

26行目:

最初に指定路線名が含まれている行を探します。検索文字列は「指定路線名

」としています。あまり意味ないかもしれませんが…

27行目:

これ以降は、指定路線名が含まれる行が見つかった場合の処理です。指定路線名が含まれていた場合、次の行に運行情報が含まれていますので、それについて調べます。

31行目:

次の行に「平常」があれば平常運行ですので、LEDの発光色は緑色、点灯したままのパターンにします。

37行目・39行目:

「平常」が含まれていない場合、次に「遅延」が含まれているか確認します。含まれていれば「列車遅延」ですので、LEDの発光色は黄色、点滅パターンにします。

44行目:

何でもない場合は運行停止などよろしくない状況ですので、LEDの発光色は赤色、点滅パターンにします。

52〜55行目:

最後に解析結果を表示します。

実際に動作確認してみます。上のファイルは “get_railwayinfo.php” というファイル名で作成、chmodで実行権限をつけて以下のように実行します。(chmodの使い方は覚えてますか? chmod +x ファイル名です)

pi@raspberrypi ~/dev $ ./get_railwayinfo.php 

山手線の運行状況LED点滅パターン
LED色: yellow
LED点滅パターン:
array(3) {
  [0]=>
  string(5) "blink"
  [1]=>
  string(5) "blink"
  [2]=>
  string(5) "blink"
}

これは、index.htmlの山手線の運行状況を「列車遅延」に書き換えて動作確認したものです。LEDの色は黄色、LED点滅パターンは点滅と判定されました。天気予報と同じようなデータにできましたので、この解析結果からLEDの点滅ができそうです。

 

Pythonで鉄道運行情報文字列解析プログラムを作成

Pythonでも同じ考え方でプログラムを作成してみました。プログラムの後に必要と思われる部分を説明します。

#!/usr/bin/python
# coding: utf-8

# モジュールインポート
import urllib2

# 情報取得する路線名設定
lineName = '山手線'

# Yahoo! Japan運行情報ページのURL
# 以下は関東地方の運行情報URL
url = "http://transit.yahoo.co.jp/traininfo/area/4/"

# Yahoo! Japan運行情報ページのHTMLデータ取得して1行ずつリストに格納
# response = urllib2.urlopen(url)
# htmlData = response.read()
# railwayInfo = htmlData.split('\n')

# ただしテスト用なのでWebからの取得はせずにローカルに保存してあるファイルを読み込む
# index.htmlをオープンして読み込み、1行ずつリストに格納
f = open('./index.html')
htmlData = f.read()
f.close()
railwayInfo = htmlData.split('\n')

# LED点滅パターンリスト初期化
ledPat = {}

# リストに入れたHTMLデータ各行を解析
for i in range(0, len(railwayInfo)):
    # 情報取得する路線名文字列が含まれているかチェック
    pos = railwayInfo[i].find(lineName+'</a></td>')
    if pos >= 0:
        # 含まれていれば次の行に運行情報文字列があるので
        # 運行情報に合わせたLEDの色とパターンを格納
        # まず「平常」が含まれるかチェック
        pos = railwayInfo[i+1].find('平常')
        if pos >= 0:
            # 平常運転であればLED色は緑で点灯
            ledPat.update({'rail_color': 'green'})
            ledPat.update({'rail_pat': ('on', 'on', 'on')})
        else:
            # 平常運転でなければ「遅延」が含まれるかチェック
            pos = railwayInfo[i+1].find('遅延')
            if pos >= 0:
                # 遅延であればLED色は黄色で点滅
                ledPat.update({'rail_color': 'yellow'})
                ledPat.update({'rail_pat': ('blink', 'blink', 'blink')})
            else:
                # 平常運転、遅延でもなければLEDを赤点滅して警告する
                ledPat.update({'rail_color': 'red'})
                ledPat.update({'rail_pat': ('blink', 'blink', 'blink')})

print lineName + 'の運行状況LED点滅パターン'
print 'LED色: ' + ledPat['rail_color']
print 'LED点滅パターン: '
print ledPat['rail_pat']

8行目:

lineName変数に、情報取得する路線名を代入しておきます。

14行目〜17行目:

これらの行は実際にYahoo! Japanの運行情報Webサイトからデータを取得する行ですが、検討段階で何度も取得しないようにコメントアウトしています。

19行目〜24行目:

その代わりにこの行でローカルに保存してあるHTMLデータを読み込みます。

24行目:

HTMLデータの各行を分解して配列に保存します。splitで行っています。元データを、splitの引数で指定した区切り文字で分解して、分解した要素を一つづすリストにします。返り値はそのリストになりますので、それをrailwayInfoに代入しています。

30行目:

このfor文で、配列に入れたHTMLデータの各行を調べていきます。

32行目:

最初に指定路線名が含まれている行を探します。検索文字列は「指定路線名

」としています。

33行目:

これ以降は、指定路線名が含まれる行が見つかった場合の処理です。指定路線名が含まれていた場合、次の行に運行情報が含まれていますので、それについて調べます。含まれていない場合は処理を繰り返します。

38行目:

次の行に「平常」があれば平常運行ですので、LEDの発光色は緑色、点灯したままのパターンにします。

45行目:

「平常」が含まれていない場合、次に「遅延」が含まれているか確認します。含まれていれば「列車遅延」ですので、LEDの発光色は黄色、点滅パターンにします。

50行目:

何でもない場合は運行停止などよろしくない状況ですので、LEDの発光色は赤色、点滅パターンにします。

54〜57行目:

最後に解析結果を表示します。

« »