PHPにおけるプログレスバーの実現2

再び、競馬の解析のPHP作成を再開しました。前はサイアーラインから色分けした血統表を作成、ってところまで作ったので、とりあえず保留。これからはオッズと着順の関係を条件ごとに検索できるシステムをちょくちょく作っていきたいと思っています。今はURL指定すれば自動で10000件くらいのレースのデータを取って来れるようにはなりました。次は取ってきたデータを解析するところを作っていきます。
話は変わってこのブログで一番アクセスが多いのがPHPのプログレスバー関連の話なので、gdgdだった前回の説明を補足、簡略化する意味で再び解説していきたいと思います。

プログレスバー?
競馬の解析をする際、情報はネットからスクレイピング(必要な部分だけ抽出してくること: はてなダイアリー)していますが、単純にウェブサイトを読み込む時間が必要なので、如何にブロードバンド時代と云えど100ページとか読み込むには相当な時間がかかります。しかもPHP側で特に何もしないと、果たして動いているのか動いていないのか分からないです(もしかしたらエラーが起きているかもしれない…)。そこでWindowsなどでファイルを移動するときに移動状況を表してくれる”アレ”がPHPでも使えたら便利じゃないか!まさにその”アレ”こそ、プログレスバーなのです。

タイムアウトするんだけど?
長い処理をするとタイムアウトしてしまうので、それを防がないとなりません。
set_time_limit(0);
こんな記述を冒頭に入れてあげましょう。


PHPの知識だけでできるの?
PHPの知識を持ってらっしゃる方は、大体HTMLの知識も持っていらっしゃると思います。プログレスバーは、PHP+JavaScriptで実現するのです。つまり、
    PHPでJavaScriptの関数を呼出 ⇒ JavaScriptでプログレスバーを動かす
といったことをやるわけです。それではPHPからどのようにして、JavaScriptを動かせば良いのでしょうか?

JavaScriptの準備
簡単のために、まずはテキストが表示される例を作成してみましょう。HTML側には以下のような記述をしてください。
<span id="msg"></span>
<script type="text/javascript">
function changeMsg(msg) {
    document.getElementById("msg").innerHTML = msg;
}
</script>
このchangeMsg()関数をPHPから呼び出し、<span>タグの中身を書き換えるわけです。

バッファについて
PHP側の記述を紹介する前に、「バッファ」について学んでおきましょう。バッファとはパソコンが処理をする際にそのデータを一時的に蓄えておくところのことです(パソコン用語 - バッファとは)。PHPで意識しなければならないバッファは2種類あります。1つ目はサーバー側のバッファで、PHPで処理しているデータを向こうが処理が完了するまでためている、なんてことがあったら処理が完了するまで途中経過のデータが見れないことになってしまいます。2つ目はブラウザ側のバッファです。読み込みが遅くて画面が真っ白だから、もうや~めた、と思って中止ボタンを押すと、途中まで表示された!なんて例を挙げてみると分かりやすいのではないでしょうか。これらを吐き出させるためにPHPで次の2行を記述しなければなりません。
ob_flush();
flush();
バッファをフラッシュ(水をどっと流す)してあげる記述です。

PHPからJavaScriptを呼び出す!
いよいよここまで来ましたね。さぁ、どうするんだ!といった感じですが、実は至って簡単なのです。
$com = "表示したい内容"
echo '<script type="text/javascript">changeMsg("' . $com . '")</script>';
Quick Lookup:
これだけです。ボブ風にいうと(ボブ知らなかったらすみませんww)、ほら、簡単でしょう?と言った感じですね。そして、お友達も書いてあげましょう、といった感じで、
sleep(1); // 重い処理する場所
$com = $i . "/" . $total; // $i: 処理中のデータ番号 $total: データ総数
echo '<script type="text/javascript">changeMsg("' . $com . '")</script>';
ob_flush();
flush();
こんな風にしちゃえばOKです。いかがでしょうか?思ったよりも簡単ですよね。具体例を挙げると、
$i=1;
foreach($url as $urls) {
    getDataFromURL($url);
    // ($urlのページからデータを取ってくる自分で定義した関数)
    $com = $i . "/" . count($datas);
    echo '<script type="text/javascript">changeMsg("' . $com . '")</script>';
    ob_flush();
    flush();
}

こんな感じです。

プログレスバーに応用したい場合は、JavaScript側で工夫さえすればPHPで何もする必要はありません。PHPにおけるプログレスバーの実現でプログレスバーを動かすサンプルを置いてあるのでぜひそちらも見てみてください。

競馬解析プログラム3

暫く、体調不良でした…、ちょっとレポートが大きかったんで。この間から進んだことは、

①上のサイアーラインにも色付けする。
この間の記事でいうと、Tom Foolの父親が緑になってませんが、あれも緑にするっていった感じです。

②全馬の父・母の頻度を調べる。
2007/11/11のエリザベス女王杯時点での去年までのデータでいうと、
FA :
19
7
2
2
2
1
MF :
12
9
5
3
2
2

こんな感じです。FAが父、MFが母父です。

③クロスを調べる。

スティルインラブ  Fa    Mo-Fa 2003/11/16 Turn-to: 5x5 Royal Charger: 6x6 Blue Swords: 6x6 
スティルインラブ サンデーサイレンス Halo Hail to Reason Turn-to Royal Charger
Source Sucree
Nothirdchance Blue Swords
Galla Colors
Cosmah Cosmic Bomb Pharamond
Banish Fear
Almahmoud Mahmoud
Arbitrator
Wishing Well Understanding Promised Land Palestinian
Mahmoudess
Pretty Ways Stymie
Pretty Jo
Mountain Flower Montparnasse Gulf Stream
Mignon
Edelweiss Hillary
Dowager
ブラダマンテ Roberto Hail to Reason Turn-to Royal Charger
Source Sucree
Nothirdchance Blue Swords
Galla Colors
Bramalea Nashua Nasrullah
Segula
Rarelea Bull Lea
Bleebok
Sulemeif Northern Dancer Nearctic Nearco
Lady Angela
Natalma Native Dancer
Almahmoud
Barely Even Creme dela Creme Olympia
Judy Rullah
Dodge Me The Doge
By Me

Frequency :
1.96875
1.9375
0.375
0.3125
0.21875
0.09375
0.09375


いまはこんな感じになってます。あ、ちなみに色も濃くしました。まだまだやることは多いっすね。目標の自動予想までの道のりは長い…、って感じです。何か付けてほしい機能とかありましたらどんどん言ってください。出来るところから対応していきたいと思います、といいつつ公開はずっと先になりそうです(汗

競馬解析プログラム2

血統表の出力までは完成しました。あれから、HTMLの解析結果をSQLiteのデータベースに格納するプログラム、解析するプログラムと2つにわけ、なんとか!血統表の出力までこぎつけました。苦労したのは、血統表の組み立ててです。↓の方にもサンプル載せましたが、形を見て分かる通りめんどくさいテーブルです。しかも、取得した馬の配列データは、テーブルのtdを上から見てった順番!ウハ、どうしよ、って感じでした。結局、
// 並び替え用
var $change = array(2,4,8,16,32,33,17,34,35,9,18,36,37,        19,38,39,5,10,20,40,41,21,42,43,11,22,44,45,23,46,47,3,
6,12,24,48,49,25,50,51,13,26,52,53,27,54,55,7,14,28,56,
57,29,58,59,15,30,60,61,31,62,63);
こんなん作って、for文で処理しちゃいましたw
        for($i=0; $i<62; $i++) {
            $this->horse[$this->change[$i]] =$_blood[$i];
        }
んで、できたテーブルが


フサイチパンドラ 2006/11/12 entried.
フサイチパンドラ サンデーサイレンス Halo Hail to Reason Turn-to Royal Charger
Source Sucree
Nothirdchance Blue Swords
Galla Colors
Cosmah Cosmic Bomb Pharamond
Banish Fear
Almahmoud Mahmoud
Arbitrator
Wishing Well Understanding Promised Land Palestinian
Mahmoudess
Pretty Ways Stymie
Pretty Jo
Mountain Flower Montparnasse Gulf Stream
Mignon
Edelweiss Hillary
Dowager
ロッタレース Nureyev Northern Dancer Nearctic Nearco
Lady Angela
Natalma Native Dancer
Almahmoud
Special Forli Aristophanes
Trevisa
Thong Nantallah
Rough Shod
Sex Appeal Buckpasser Tom Fool Menow
Gaga
Busanda War Admiral
Businesslike
Best in Show Traffic Judge Alibhai
Traffic Court
Stolen Hour Mr. Busher
Late Date
2.21875
0.96875
0.875
0.75
0.09375
0.09375



こんなんです。色の付け方は、自分自身を①番として親を2代目、祖父母を3代目…、と見ていき順に②、③…と番号を振っていくと、ある特定の馬から見た父親と母親はそれぞれ自分の番号×2、自分の番号×2+1とすることができます。そこでこれを使って自分からスタートして再帰的に解析していくことによって全馬調べることができます。テーブルの作り方は、自分の番号が何世代目の番号か調べることによってtdのrowspanを指定して組み立てることができます。細かいことはちょっとメンドイのでw、おいておきます。すみません。とりあえず、これでエリザベス女王杯についての過去の血統表が完成しました。後は、クロスとかニックスとかの解析もしてみようかな。

なお、色付けに関してはブラッドバリエーション様のデータを参考にさせていただきました。ありがとうございます。

競馬解析プログラム

このブログのデザインもだんだん飽きてきんで(笑)、そろそろ本筋に入って行こうかと思います。当面の目標は、「各馬・レースのデータベースの構築」⇒「自動的に次のレースの予想をはじき出す」というプログラムの作成です。データベースの構築といっても、一個一個手入力でやっていくには膨大な時間の無駄になってしまうのは、前に作った解析システム(高校の友達と一緒にやってました)から学びました。そこで今回は情報を配信しているサイトをクロールして、情報を収集するプログラムを作成しようと思います。言語はPHPでやって行こうと思っています。

最初に突き当たった壁が、どうやってHTMLを解析するのか。HTMLのパーサーのクラスライブラリとかないかな、と思ってところ、PEARパッケージのXML_HTMLSaxを紹介しているサイトが多かったので試してみました…が、PHPに触りたての自分にとってはちょっち無理w、ってことであきらめ、色んなクラスライブラリも試してみましたが、なかなかうまくいかず…。カッコイイ関数とか使わなくて、単純な仕組みでやってくれてるのないかな、と思ってたところ、オライリー社のSpidering Hacksという本のソースコードをDLできるページがあることを知り、早速使ってみました。…すると、どんぴしゃっすね。若干自分で改変しましたがstrpos()(:引数で指定した文字列のある場所を返す)を使うだけで特定の箇所を抜き出すことを実現していました。

function getBlock( $pStart, $pStop, $pSource, $offset) {
   $_data = null;
   $_start = stripos( $pSource, $pStart, $offset);
   $_stop = stripos($pSource, $pStop, $_start);
   $start = $_start + strlen($pStart);
   $length = $_stop - $start;
   if($_stop > $_start ) {
      $_data = trim( substr( $pSource, $start, $length ) );
   }
  
   return( array($_data, $_stop) );
}

こんな感じで、$pStartと$pStopで指定したものの間を抜き取るって感じです。まぁ同じタグが複数個所あったら抜き出すのは難しいですが、最近のHTMLはクラス、ID指定とかで特定の部位が探し出しやすくなっていることが多いので、以外に使えます。これなら、HTML⇒XHTML⇒XMLとして解析、とかキャパ越えなことしなくても大丈夫!ってことでいろいろ試してみました。その結果、とりあえず

No.0 テイエムオペラオー

栗毛
1996/03/13
オペラハウス
Blushing Groom

No.1 ディープインパクト

鹿毛
2002/03/25
サンデーサイレンス
Alzao

No.2ゼンノロブロイ

黒鹿毛
2000/03/27
サンデーサイレンス
マイニング
・・・
みたいな感じで、情報を抜き出していくことは可能になりました。まー、あとは抽出する項目を増やしていきましょ、って感じです。…ただし、対象のHTMLを直接読み込んでるんで、こちらも相当時間かかりますし(普通にタイムアウトしますww)、情報を抜き出させてもらっているサイト様にも迷惑かと…。問題は山積みですが、頑張って行こうと思います。