Webページへの公開情報を自動的に抽出・取得する

June 5, 2009 – 5:21 pm

Webページには、さまざまなデータ・情報が公開されている。商品情報、その価格、為替レート、株価などなどだ。こうした情報を定期的に自動取得できる仕組みを作っておけば、情報の有効な活用ができる。複数のWebページから取得したデータを独自の視点から組み合わせ、自分・独自の情報システムを構築することだって可能だ。Reuterのニュースサイトから為替レートを自動取得する方法を試し、その可能性を考えてみた。

情報を抽出・取得するには、どういう方法があるかgoogleで検索してみた。検索された記事のひとつに、Yahoo financeのWebページからcurrency rateを引き出す方法を書いたもの(currency conversion API on a shoestring: http://www.wait-till-i.com/category/hacks/)があった。phpを使えば、簡単に情報の抽出・取得ができそうだ。

この記事で紹介している方法では、phpのcURLモジュールと正規表現(regular expression)の関数:preg_match_all()を活用している。これに習って、Reuterのニュースサイトからcurrency rateを取得してみることにした。

WebページのHTML文書の取得する: 情報の抽出に先立ち、取得したいデータを含むWebページを特定することが必要だ。Reuterのニュースサイトを見ると、為替情報を提供しているのは、Webページhttp://www.reuters.com/finance/currencies だ。

このWebページには、「currency converter」なるツール、主要通貨(7通貨)間の交換レートテーブルなどが提供されている。「currency converter」を用いると50種の通貨間の交換レートを取得できる。

「currency converter」により交換レートを求めようとする場合、通貨略号などをURLクエリーとして与えることにより、呼び出されたWebページ上に交換レートが示される。米ドルを日本円に変換するには、クエリー込みのURL:

http://www.reuters.com/finance/currencies?srcAmt=1.0&srcCurr=USD&destCurr=JPY

を投げればよい。

欲しい情報が含まれるWebページを特定すると、次に、このページを構成するHTML文書を解析可能な形でとりこむことになる。これには、phpのcURLモジュールを使うことができる。http://www.reuters.com/finance/currencies のHTML文書を取りこむには:

    $url ="http://www.reuters.com/finance/currencies";
    $ch  = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $feed = curl_exec($ch);
    curl_close($ch);

 で行える。これにより、読み取られたHTML文書は、変数$feedにとりこまれる。cURLのモジュールに含まれる関数についての説明はphpのマニュアルを参照されたい。

PHPの正規表現・関数を用いた情報の抽出: 次に、変数$feedに取り込まれたHTML文書のなかから抽出しようとする情報(この場合には、currency exchange rate)が、どういった形式でHTML文書上にくりこまれているかを知ることになる。

「currency converter」を動作させ、1米ドルに対応する日本円を求めた場合のHTML文書のなかで、目的とする交換レート情報が含まれているレコードを探してみると、

<input id=”destAmt” name=”destAmt” type=”text” value=”96.099998″/>

がある。これから1米ドルあたりの日本円に対応する96.099998という数値情報を取り出せばよいわけだ。上にあげたパターンを念頭に、$feedに取り込んだHTML文書のなかから、<input id= ・・・・ value=” と “/ に囲まれている文字列を、正規表現の関数preg_match_all()を使って求めてみる。マッチした文字列が配列$cellにセットされるようにし、これをコマンド print_r で出力するには以下の形が用いられる;

preg_match_all("/<input id=.*\" value=\"([^>]+)\"/", $feed, $cell, PREG_PATTERN_ORDER);
print_r $cell;

Array
(
    [0] => Array
        (
            [0] => <input id="srcAmt" name="srcAmt" type="text" value="1.0"
            [1] => <input id="destAmt" name="destAmt" type="text" value="96.089996"
        )

    [1] => Array
        (
            [0] => 1.0
            [1] => 96.089996
        )

)

 ここに示されるように、<input id= ・・・・ value=” で始まり “/ で終わるパターンにマッチするものが2種類あることがわかる。これらは、夫々、$cell[0][0]、$cell[0][1]に格納されている。これらふたつの文字列のなかの、<input id= ・・・・ value=” と “/ に囲まれた文字列は1.0 と96.089996 であり、これらは$cell[1][0]、$cell[1][1]にそれぞれ格納されている。なお、正規表現の関数 preg_match_all() の機能詳細はphpのマニュアルを参照して欲しい。

こうして、今、求めようとしていた米ドルの日本円の交換レートは、上記の例では、$cell[1][1]にセットされたわけだ。

通貨交換レート取得用のphp関数の作成: 上記の情報抽出の手続きを使うと簡単に、通貨交換レート取得用のphp関数を作成することができる。このphp関数は、若干の相違はあるものの、この記事で参考にした

(currency conversion API on a shoestring: http://www.wait-till-i.com/category/hacks/

に示されているものと基本的には同等である。以下、そのソースを示しておく;

<?php
function convert($from,$to){
    $url ="http://www.reuters.com/finance/currencies?srcAmt=1.0&srcCurr=".$from."&destCurr=".$to;
    $ch  = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $feed = curl_exec($ch);
    curl_close($ch);

    $n = preg_match_all("/<input id=\"destAmt.*\" value=\"([^<]+)\"/", $feed, $cell, PREG_PATTERN_ORDER);
    return $cell[1][1];
}
echo convert('USD', 'JPY') . "\n";
?>

主要通貨のクロスレートデータを一括して取得しようとする場合にも、当然のことながら、上記した情報の取得・抽出方法を用いることができる。以下、ReuterのWebページに掲げられているクロスレート情報を配列crossrates[][]に返す関数 getcrossrates()を作成してみたので、そのソースと出力例を以下に与えておく;

<?php
function getcrossrates() {
    $url = 'http://www.reuters.com/finance/currencies';
    $ch  = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $feed = curl_exec($ch);
    curl_close($ch);
    $n = preg_match_all("/<th>([^<]+)<\/th>/",$feed,$cell_currency, PREG_PATTERN_ORDER);
    $n = preg_match_all("/<td class=\"data.*>([^<]+)<\/td>/",$feed,$cell_rate, PREG_PATTERN_ORDER);
    $currency = array();
    for ($i=1; $i<8; $i++ ) {
        $currency[$i-1] = $cell_currency[1][$i];
    }
    for ($m=0; $m<$n; $m++) {
       if ( strstr($cell_rate[1][$m], $currency[0]) ) break;
    }
    $rate = array();
    for ($i=0; $i<7; $i++ ) {
      for ($j=0; $j<7; $j++ ) {
         $k = $i*8+$j+$m+1;
         $rate[$i][$j] = trim($cell_rate[1][$k]);
      }
    }
    return $rate;
}
$crossrates=getcrossrates();
for ($i=0; $i<7; $i++) {
 for ($j=0; $j<7; $j++ ) {
   echo $crossrates[$i][$j] . "   ";
 }
 echo "\n";
}
?>

出力例:

1.0   96.7300   0.7037   1.0988   0.6221   1.2424   1.0683
0.010333   1.0   0.007274   0.011352   0.006423   0.012840   0.011039
1.4208   137.4700   1.0   1.5616   0.8831   1.7659   1.5179
0.9095   88.0200   0.6400   1.0   0.5655   1.1303   0.9718
1.6084   155.3800   1.1318   1.7675   1.0   1.9991   1.7181
0.8044   77.8200   0.5660   0.8840   0.4999   1.0   0.8591
0.9358   90.5200   0.6585   1.0284   0.5818   1.1631   1.0

本方式による情報の抽出・取得法の限界と問題点: 具体的な例を通じ、Webページからの情報の抽出・取得が比較的容易に行えるのが理解できた。しかし、この方法、当然のことながら、情報を提供しているWebサイトの構成が変わってしまうと正しく動作しなくなってしまう。このあたりが、Amazon ECSなど情報の取得を前提にapiを公開している場合と通常の公開Webから情報を取得しようとする場合とで、決定的に異なるところだろう。通常の公開Webから情報を取得しようとする際には、常に、情報源とするWebサイトの動向をチェックしておかなければならない。

さらに、もうひとつの問題点として、著作権の問題がある。この問題かなり複雑だ。上記の例で対象としたReuterのニュースサイトの著作権について、次のような記述がある;

ユーザーは、自己の個人的使用及び非商用目的に限り、このサイトにおけるコンテンツの抜粋をダウンロードまたは印刷することができます。ロイターが事前に書面により承認した場合を除き、ロイター・コンテンツを再発行や再配布すること(フレーミングまたは類似の方法による場合を含む)は、明示的に禁止されています。

この記述から、WebページからHTML文書をダウンロードし、これから為替レートなどの数値情報を抽出すること自体は問題なさそうだ。しかし、「ロイター・・コンテンツを再発行や再配布することは、明示的に禁止されています。」とある。これをどのように理解すべきか?

上記したかたちで抽出・取得した為替情報を他の情報と合わせて加工するような場合はどういうことになるのだろうか?個人的な使用する場合は問題ないとしても、加工した情報をWeb上で公開するような場合はどうなんだろう?かなりグレーかもしれない。結構、複雑な問題だ。ただ、為替の交換レートといったような数字というかたちで事実を公表しているようなもの、これが著作権に抵触するとは考えにくい。そうはいっても、明確な線がどこにあるのかはっきりしておかねばならないだろう。

著作権の問題クリアにしておかないと、とんでもない話になることだって否定できない。このあたりをクリアにしておくことができれば、冒頭に述べた独自の情報システム、もっと言えば情報発信サイトの構築も可能になる。