続・競馬であそぶ 3

競馬データ集めるだけでは、ただのゴミ


  1. フリーハンデ(2006.01.13):(02.24):(05.06):(2007.02.08)
  2. DBMファイルの使い方(2006.01.17)
  3. 自動処理(2006.01.21)
  4. pycron の設定(2006.01.21)
  5. 時系列データ(2006.02.02)
  6. 乗替り初騎乗抽出スクリプト(2006.02.08)
  7. 高レベルレースの抽出(2006.03.01)
  8. コマンドプロンプト画面のトラブルを回避(2006.04.14)
  9. 偏差値(2006.05.11) 
  10. だそく (^・・^)
---------------------------------

トップページへ




0.  インターネットでの情報

折角、Perl をインストールしているのなら、競馬の情報をインターネットから集めるの も楽なのでは?といわれる。 楽なのかもしれないが、自分の場合はブラウザでデータを確かめて必要なら取り込み、 それを Perl で加工するという方法で今のところ間に合っている。
ただ、このところ血統などに興味を持ちはじめてから、やり方くらい覚えておいた方が いいかな、という気分にはなってきた。

検索から加工までのすべてを自動で、とは思わないけど面倒な作業はなるべく Perl に やってもらいたい。 スパイダリングとかスクレーピングなどというレベルは望んでも いないが、データであそぶのに苦労を強いられるようじゃ面白くない。

で、このシリーズでは情報の取得〜整形の初歩をほんの少しだけ、かじろうと思う。 いまのところ五里霧中ではあるが、Perl ならなんとかなると信じて。 今までよりもペースダウン確実でも他に処理すべき対象が少なくなってきた ことだし、ボチボチやりやすいところから。

Active Perl にも標準で LWP モジュール( Library for WWW in Perl ) が組み 込まれている筈なので、確かに使わないと損かも、



1. フリーハンデ 

Perl でスクリプトを書いていて、たまに閃いて新しい発見をしたような気になることがある。 大抵は、他の人がもっとイイ方法を公開していたり、タダの勘違い。

新しく何かをやろうとするときは、モジュールにどんなものがあるかをマズ調べる。 便利なツールが一杯あって誰でも使えるし、使い方のサンプルも沢山ある。 沢山ありすぎるので困ることもある。 Perl のモジュール群は高いレベルでの発明発見の集積みたいなもんだ。

ということで、見様見真似で挑戦開始。最初は、一番単純な Simple で行きましょう。
まず、合同フリーハンデ・オフィシャルウェブサイトから新しいフリーハンデのページを 取得します。

但し、url や HTML内のデータの配置は時期によって異なります。
存在しないページを取得しに行っても仕方ありません。確認します。
以下を、コピーして( fh.pl とでも名付けて )コマンドラインから実行します。
> perl fh.pl
OKであれば、そのページは存在するのでカレントに取得します。

#!/usr/bin/perl -w

use strict ;
use LWP::Simple ;

my @urls = (
    # 合同フリーハンデ3歳(1〜4)
    'http://www.keibabook.co.jp/homepage/fh03_1.html',
    'http://www.keibabook.co.jp/homepage/fh03_2.html',
    'http://www.keibabook.co.jp/homepage/fh03_3.html',
    'http://www.keibabook.co.jp/homepage/fh03_4.html',
    # 合同フリーハンデ4歳以上(1〜7)
    'http://www.keibabook.co.jp/homepage/fh04_1.html',
    'http://www.keibabook.co.jp/homepage/fh04_2.html',
    'http://www.keibabook.co.jp/homepage/fh04_3.html',
    'http://www.keibabook.co.jp/homepage/fh04_4.html',
    'http://www.keibabook.co.jp/homepage/fh04_5.html',
    'http://www.keibabook.co.jp/homepage/fh04_6.html',
    'http://www.keibabook.co.jp/homepage/fh04_7.html',
) ;

for my $url ( @urls ) {
    my $fname = ( split '/', $url )[-1] ;
    if ( head($url) ) {
        print "$fname OK!\n" ;
        getstore $url, "$fname" ;
    } else {
        print "$fname は存在しません!\n" ;
    }
}

ページ( htmlファイル )が取得できたら、次は中のデータの配置などをチェックします。
どういう形で出力するかも、考えないといけません。以下は出力スクリプトの例です。
フォーマットは自分の都合のいい形にすればいいでしょう。

#!/usr/bin/perl -w
use strict ;

my @htmls = (
    'fh03_1.html',  'fh03_2.html',  'fh03_3.html',  'fh03_4.html',
    'fh04_1.html',  'fh04_2.html',  'fh04_3.html',  'fh04_4.html',
    'fh04_5.html',  'fh04_6.html',  'fh04_7.html',
    ) ;
my $outF = 'freeh.dat' ;
open OUT, ">$outF" ;
my $count = 0 ;
my @dat = () ;
for my $fn ( @htmls ) {
    open DAT, $fn or die "ファイル $fn がありません。。$!\n" ;
    while( <DAT> ){
        chomp ;
        next unless /^<(TD class="mfont" align|\/*TR)/ ;
        if ( /^<TR>/ ) {
            $count = 0 ;
            @dat = () ;
            next ;
        }
        if ( /^<\/TR>/ ) {
            if ( $count > 14 ) {
                my ( $mk, $jun, $sx, $max, $vS, $vM, $vI, $vL, $vE, $hnd,
                     $tdmk, $td, $una ) = @dat ;
                $una =~ s/USA|IRE|GB|FR|AUS// ;
                $una = substr( $una, 0, 18 ) ;
                my $line = sprintf "%-18s %3d %s %3s %3s %3s %3s %3s %3s %4s %2s%s",
                    $una, $jun, $sx, $max, $vS, $vM, $vI, $vL, $vE, $hnd, $tdmk, $td ;
#               csv 出力時は、上2行をコメントアウトして、下2行の # を外す。(例)
#               my $line = join ",",
#                   $una, $jun, $sx, $max, $vS, $vM, $vI, $vL, $vE, $hnd, $tdmk, $td ;
                print OUT $line, "\n" ;
            }
            $count = 0 ;
            @dat = () ;
        }
        if ( /^<TD/ ) {
            s/<\/FONT>// ;
            my ( $dat ) = ( split '">|<\/', $_ )[-2] ;
            $dat[$count] = $dat ;
            $count++ ;
            next ;
        }
    }
    close DAT ;
}
close OUT ;

以下が上記スクリプトでの実際の出力です。
エクセルなどで使うのなら、カンマ区切りにすればいいんじゃないでしょうか。

ディープインパクト   1 L 126       120 126 120 68.0  T
カネヒキリ           2 I 119    117 119       64.5  #d
インティライミ       3 L 118          118    64.0  T
アドマイヤジャパン   4 E 116       113 110 116 63.0  T
シーチャリオット     4 I 116    110 116       63.0  d
シーザリオ           6 I 115    111 115 111    62.5  T
シックスセンス       6 I 115    101 115 115 108 62.5  T
 〜以下省略

(2006.01.14) フリーハンデ取得〜加工用スクリプト (リンクを切りました)

いくらか書き換えました。うまく活用 & 応用してください。



新たな修正版が出ているので、取得用とDBMファイル用をこちらに。

(2006.05.06) フリーハンデ取得〜加工用スクリプト (リンクを切りました)

2006年4月30日修正版用。 2006.05.06 現在

5/31 修正分に関しては、一部修正( 3歳を1ページ追加だけ )で OK 。
ということで、あとは適当に、、
6/30 修正分は前月と多分、同じ。これ以後は面倒なので省略。

(2007.02.08) フリーハンデ取得〜DBM加工用スクリプト (リンクを切りました)


 ↑ は、2006年確定版用ということで、とりあえず書いたけど自由に書き直してくらはい。

対象馬は殆どオープンクラスなので使い方が限定されるのですが、自分の指数と比較したり、 上級
レースの出馬表で参考データとして表示させるようにするのも面白いのではないでしょうか。
表示させるための具体的な方法については、次項でポイントだけを説明するつもりです。



2.  DBMファイルの使い方 

DBMファイルは、ハッシュデータを保存したディスク上のファイルのことです。 Database Management の略。
ハッシュというのは値をたくさん(or 少し)集めたもので、必要なときにはキーを使って抜き出します。
騎手コードをキーにして騎手名を出したり、逆に馬名をキーにして血統登録番号を知ったりという ような使い方をします。

%hash ;
$hash{ $key } = $value ;

Perl には dbmopen という、関数があります。これは DBMファイルをハッシュに 結合する時に使います。
どういうことかというと、スクリプトで使われるハッシュはメモリー内での始動〜完結が普通ですが、 それでは都合が悪いことがあります。
例えば、外人騎手の場合などでは短期騎手免許が切れるとその騎手のデータが非蓄積系では手に入れ づらくなります。負傷欠場中の騎手も、同様です。現在(2006.01.27)は横山典騎手など。
騎手の巧拙を、提供データから毎回計算するようなスクリプトを書いた場合、今回乗り替った騎手との 比較ができなくなったりするわけです。

そんな場合でも、DBMファイルとハッシュを結合させておけば、新しいデータは追加更新(or 無視)され ますが、古いデータはそのままファイルに残るので解決です。
現在は、暫定で修正用の騎手データ( kshyk2.dat )を併用しているので問題なく見えているだけです。

この騎手ポイントに関する DBMファイルへの切り替えはまだやっていません。
とりあえず、中央以外での過去の戦績では騎手コードが提供されていないので、騎手名をキーに して騎手コードを牽くような DBM を作ってテストしています。
移行が楽なので、この方法でいくかもしれません。 騎手の同姓同名という例もあるので、絶対ではありませんが、、

馬名をキーにした血統登録番号というデータを作る場合でも、新しい馬名があれば追加という形で 簡単にできます。
ただ、これをやり始めると抹消馬の処理などで蓄積系のデータにまで手を伸ばさざるを得なくなり、 そうなるとヤッパリ RDB かなぁ、なんてことにならないとも限らないし、ちょっと迷う。
とは言っても、考えてみると使い道がないですね。やり方だけ以下に。

血統登録番号用のスクリプトです。名前は一応、una2cd.pl としています。

#!/usr/bin/perl
# 
# 馬名から血統登録番号が必要になることが万一あれば、、
# DBM ファイルのテスト。データが蓄積されていく、、
# Usage:     perl una2cd.pl yymmdd
# yymmdd は、UMPWファイルの年月日の部分を( 6 or 8 )桁で。
# DB名 は、umacd
# umacd.dirumacd.pag の二つのファイルが出来る。
# 以下パス等は環境に応じて書き換えること。
# 一括処理( スクリプト書換え or バッチファイル )は省略。

use strict ;
my %umas ;
my $dbpass = 'H:/DB' ;          # ★ DBM ファイル収納場所
my $dtpass = 'H:/TDAT' ;        # ★ 週データパス(UMPWファイル所在地)
        # ここでは、フォルダ名に西暦年が付いていると想定しているので注

my $ymd = shift @ARGV ;
$ymd += 20000000 if $ymd < 10000000 ;
my $yyyy = substr( $ymd, 0, 4 ) ;               # 注
my $fname = "$dtpass/$yyyy/UMPW$ymd\.dat" ;     # 注
dbmopen %umas, "$dbpass/umacd", 0666 or die "Can't open umacd: $!\n" ;
open DAT, $fname or die " そんなファイル ${fname} はナイだよ。\n" ;
while( <DAT> ) {
    chomp ;
    my ( $ucd, $bname ) = unpack '@11 A10 @46 A18', $_ ;
    $bname =~ s/ //g ;
    $umas{ $bname } = $ucd ;
}
close DAT ;
dbmclose %umas ;

どんなデータ( キー&値 )が保存されているか、少し整形して出力しましょう。test.pl

#!/usr/bin/perl

# DBM出力テスト
# Usage:     perl test.pl

my $dbpass = 'H:/DB' ;      # ★DBM 所在地
my $dbname = 'umacd' ;      # ★DBM の名前

my %test ;                      # 仮のハッシュ名
my $outF = $dbname . '.dat' ;   # 出力ファイル名
open OUT, ">$outF" ;
dbmopen %test, "$dbpass/$dbname", 0666 or die "Can't open DBMs: $!\n" ;
while( ( $key, $value ) = each( %test ) ) {
#   print OUT "$key:$value\n" ;
    printf OUT "%-18s:%s\n", $key, $value ;     # 整形しています。
}
close OUT ;
dbmclose %test ;

こんな感じです。

デルマフローラ    :2002100320
テイエムカリスマ  :2002104853
スズランサイレンス:2003103825
ホクテンジャチクン:2002103262
        ..以下略


こみいった話になってきたので、この辺で切り上げます。あとは、参考例ということで、
上で作った DBM ファイルを使って、フリーハンデ用の DBM ファイルを作ってみます。

#!/usr/bin/perl
# 
# フリーハンデを dbm ファイルに。# DBM ファイルのテスト。
# Usage:     perl freehnd.pl
# 以下★パス等は環境に応じて書き換えること。
# DB名 は、freehnd
# 馬コードをキーにしたハッシュを作っている。
# つまり、馬コードが不明なものは除外。

use strict ;
my $dbpass = 'H:/DB' ;              # ★ DBM ファイル置き場
my $dtpass = 'H:/KB_SC/html' ;      # ★ フリーハンデのデータパス
my %fhd ;                       # 新しく作る DBM ファイル用のハッシュ
my %umcd ;                      # DBMファイル置き場にある 馬コードハッシュ 

my @htmls = (
    'fh03_1.html',  'fh03_2.html',  'fh03_3.html',  'fh03_4.html',
    'fh04_1.html',  'fh04_2.html',  'fh04_3.html',  'fh04_4.html',
    'fh04_5.html',  'fh04_6.html',  'fh04_7.html',
    ) ;
my %b2s = ( 'S' => 'S', 'M' => 'M', 'I' => 'I',  'L' => 'L','E' => 'E' ) ;

dbmopen %fhd, "$dbpass/freehnd", 0666 or die "Can't open freehnd: $!\n" ;
dbmopen %umcd, "$dbpass/umacd", 0666 or die "Can't open freehnd: $!\n" ;

my $count = 0 ;
my @dat = () ;
for my $fn ( @htmls ) {
    my $file = "$dtpass/$fn" ;
    open DAT, $file or die "ファイル $fn がありません。。$!\n" ;
    while( <DAT> ){
        chomp ;
        next unless /^<(TD class="mfont" align|\/*TR)/ ;
        if ( /^<TR>/ ) {
            $count = 0 ;
            @dat = () ;
            next ;
        }
        if ( /^<\/TRY>/ ) {
            if ( $count > 14 ) {
                my ( $mk, $jun, $sx, $max, $vS, $vM, $vI, $vL, $vE, $hnd,
                     $tdmk, $td, $una ) = @dat ;
                $una =~ s/USA|IRE|GB|FR|AUS// ;     # 不要文字を削除
                $una = substr( $una, 0, 18 ) ;
                $sx = $b2s{$sx} ;
                my $ucd = $umcd{$una} ;
                my $line = sprintf "%3d%s %3s %3s %3s %3s %3s %1s%s %s",
                    $max, $sx, $vS, $vM, $vI, $vL, $vE, $tdmk, $td, $una ;
                $fhd{$ucd} ||= $line if $ucd ;
            }
            $count = 0 ;
            @dat = () ;
        }
        if ( /^<TD/ ) {
            s/<\/FONT>// ;
            s/ //g ;
            my ( $dat ) = ( split '">|<\/', $_ )[-2] ;
            $dat[$count] = $dat ;
            $count++ ;
        }
    }
    close DAT ;
}
dbmclose %fhd ;
dbmclose %umcd ;

中味を、test.pl で出力するとこんな感じです。

2002100991        :109I         109 108 109  T ローゼンクロイツ
2002107008        : 97M      97              T マチカネオーラ
2002110198        : 96M      96              T マルターズビクター
2002106139        : 90I          90          T ウインアルディート
            ..以下略

便利というか思ったより簡単に使えそうです。 情報取得〜DBM ファイル作成なら一本のスクリプトで書けますね。
ただ、これだと血統登録番号が徐々に判明して追加という状況を考えると、バラバラの方が良さそうです。

ホントはこんな回りくどいことをしなくても、直接馬名をキーにしたハッシュを 作ればいいのです。
ただ、そうすると本体のスクリプトでマルチバイトの呪いを解くのが少し面倒かもしれません。
こちらのテストでは、問題なく作動していますが、この問題を整理しておかないといけない段階に差し掛かってきたようです。


トップページへ 次へ