競馬サイトから結果情報をJSON形式で抜き取るJSコード(JRAサイト編)

2017年10月23日

Javascript テクノロジー プログラミング

競馬ってギャンブルだという印象を持っていましたが、非常にデータ重視の思考を持つ確率と統計に基づきたエンターテイメントという事が、最近人から教えていただいてわかってきました。 個人的にギャンブルは全く性に合っていないので、ギャンブル要素には興味ないのですが、競馬のデータに関するところは、数学的に興味を持つと面白いため、簡単にデータを取得するスクリプトを作ってみました。

競馬のデータについて

競馬のことを何も知らない僕は競馬のデータというと、馬と騎手とレース結果の順位ぐらいに考えていましたが、それらの詳細データに加えて、調教師や、レース場、天候、など非常に細かなデータが存在する事を知りました。 競馬新聞を見たことがある人は、その情報の多さを理解できると思います。 いくつかの競馬情報サイトでは、1ページでは収まらないデータがサイトを埋め尽くしていることがりかいできます。

一番重要なデータは?

全てのデータをマイニングして予想に繋げるという事も面白そうですが、とりあえず、僕の注目したいデータは、競馬のレース開催情報とレース結果のデータです。 一番基本のデータで、入り口と出口という性質の為、これらのデータを集めてみましょう。 ・・・が、これだけでも非常にデータ数が多いし、いくつものページを遷移して情報を取得しなければいけません。

JRAサイトについて

試しにJRAサイトを見てみましょう。 http://www.jra.go.jp/ 試しに2017年10月22日(日)のレース開催情報を見てみると、1日の中で東京競馬だけで、12回ほどレースが開かれている事がわかります。 ※レース情報は、JRAサイトでリンクを遷移して確認してみてください。 情報ページまでの遷移サンプル TOP > 今週の開催お知らせ > 出馬表 > ※競馬場 競馬素人の僕としては、こうした事すら全く知りませんでした。 そして、更にその中の1レースを見てみると、下記のような情報が掲載されています。 この情報だけでもかなりお腹いっぱいですね。

結果情報について

そして、最も競馬で重要な情報がレース結果の情報です。買った負けたという事と同時に、投資した馬券がいくらで払い戻されるのかが一番の興味本位になります。 実際の結果情報ページを見てみましょう。 TOP > レース結果 > ※競馬場 そして、さらにその中の1レースのみの情報は以下。

1日分のデータ

思いの外情報量があってビックリしましたが、今回は払い戻しに必要な情報がレース結果の上位3位までという事と、できれば1日分をまとめて取得したいので、サイト内を探してみたら以下のようなページがありました。 TOP > レース結果 > 払戻金 > ※競馬場 このページであれば、レース場ごとの1日の結果情報が取得できそうです。

取得JSコード

Chromeブラウザでこのページを開いた状態でデバッグコンソールに下記ソースコードをコピペして実行します。 (function(){ var $$ = function(){ $$.prototype.page2result(); }; // [console-tool] 競馬結果サイトからデータを引き抜き、JSON形式でprintする $$.prototype.page2result = function(){ var tables = document.getElementsByTagName("table"); var main = {}; var data = []; // extra date for(var i=0; i<tables.length; i++){ // get main if(typeof main.y === "undefined"){ main = $$.prototype.getMainData(tables[i]); } else{ // get-date var d2 = $$.prototype.getRaceData(tables[i]); if(d2 !== null){ data.push(d2); } } } var full = {"main":main , "data":data}; console.log(JSON.stringify(full,null," ")); }; // レース見出し取得 $$.prototype.getMainData = function(table){ var main = {}; if(!table){return main;} var target = table.querySelector("tbody > tr > td:nth-child(2)"); if( target === null ){return main;} var ptn = new RegExp("(.+)年(.+)月(.+)日\((.+)\) (.+)回(.+)([0-9]+)日"); if(target.innerText.match(ptn)){ main.y = RegExp.$1; // 年 main.m = RegExp.$2; // 月 main.d = RegExp.$3; // 日 main.w = RegExp.$4; // 曜日 main.r = RegExp.$5; // 回 main.p = RegExp.$6; // 場所 main.a = RegExp.$7; // 日 } return main; }; // レース結果情報の取得 $$.prototype.getRaceData = function(table){ var data = null; if(!table){return null;} var target = table.querySelector("tbody > tr > td > p"); if( target === null ){return null;} var ptn = new RegExp("([0-9]+)R(.+)"); if(target.innerText.match(ptn)){ data = {}; data.raceNo = RegExp.$1; data.name = RegExp.$2; data.uma = $$.prototype.getRaceResult_uma(table.nextElementSibling); data.wak = $$.prototype.getRaceResult_wak(table.nextElementSibling); data.result = $$.prototype.getRaceResult(table.nextElementSibling); } return data; }; // $$.prototype.getRaceResult_uma = function(table){ var data = []; if(!table){return data;} var target = table.querySelector("tr.gray12_h"); var targets = target.parentNode.getElementsByTagName("tr"); for(var i=1; i<targets.length; i++){ var td1 = targets[i].querySelector("td:nth-child(1)"); var td3 = targets[i].querySelector("td:nth-child(3)"); data.push({no:td1.innerText , number:td3.innerText}); } return data; }; $$.prototype.getRaceResult_wak = function(table){ var data = []; if(!table){return data;} var target = table.querySelector("tr.gray12_h"); var targets = target.parentNode.getElementsByTagName("tr"); for(var i=1; i<targets.length; i++){ var td1 = targets[i].querySelector("td:nth-child(1)"); var img = targets[i].querySelector("td:nth-child(2) img"); data.push({no:td1.innerText , number:img.getAttribute("alt")}); } return data; }; $$.prototype.getRaceResult = function(table){ var data = []; if(!table){return data;} var targets = table.querySelectorAll("tbody > tr:nth-child(3) > td:nth-child(2) > table > tbody > tr"); for(var i=0; i<targets.length; i++){ var td = targets[i].getElementsByTagName("td"); if(td.length !== 8){continue;} data.push({"name":td[0].innerText , "result":td[1].innerText , "price":$$.prototype.web2num(td[2].innerText)}); data.push({"name":td[4].innerText , "result":td[5].innerText , "price":$$.prototype.web2num(td[6].innerText)}); } return data; }; $$.prototype.web2num = function(txts){ var arr = []; var sp = txts.split("\n"); for(var i=0; i<sp.length; i++){ arr.push($$.prototype.txt2num(sp[i])); } return arr.join("\n"); }; $$.prototype.txt2num = function(str){ var str1 = str; str1 = str1.split("円").join(""); str1 = str1.split(",").join(""); str1 = Number(str1); if(str1 === null){ return str; } else{ return str1; } }; new $$(); })(); ※ほとんどコメントを書いていないので、詳細が知りたい人はコメントかメールなどでご質問ください。 実行結果は下記のようになります。 { "main": { "y": "2017", "m": "10", "d": "21", "w": "土", "r": "4", "p": "東京", "a": "6" }, "data": [ { "raceNo": "1", "name": " 2歳 未勝利 [指定]", "uma": [ { "no": "1", "number": "5" }, { "no": "2", "number": "1" }, { "no": "3", "number": "8" } ], "wak": [ { "no": "1", "number": "3" }, { "no": "2", "number": "1" }, { "no": "3", "number": "4" } ], "result": [ { "name": "単勝", "result": "5", "price": "1300" }, { "name": "馬単", "result": "5-1", "price": "6670" }, { "name": "複勝", "result": "5\n1\n8", "price": "260\n170\n110" }, { "name": "ワイド", "result": "1-5\n5-8\n1-8", "price": "1120\n630\n260" }, { "name": "枠連", "result": "1-3", "price": "2760" }, { "name": "3連複", "result": "1-5-8", "price": "2160" }, { "name": "馬連", "result": "1-5", "price": "3480" }, { "name": "3連単", "result": "5-1-8", "price": "19920" } ] }, { "raceNo": "2", "name": " 2歳 未勝利 牝[指定]", "uma": [ { "no": "1", "number": "5" }, { "no": "2", "number": "10" }, { "no": "3", "number": "3" } ], "wak": [ { "no": "1", "number": "4" }, { "no": "2", "number": "7" }, { "no": "3", "number": "3" } ], "result": [ { "name": "単勝", "result": "5", "price": "520" }, { "name": "馬単", "result": "5-10", "price": "2340" }, { "name": "複勝", "result": "5\n10\n3", "price": "230\n180\n410" }, { "name": "ワイド", "result": "5-10\n3-5\n3-10", "price": "570\n1350\n1120" }, { "name": "枠連", "result": "4-7", "price": "1130" }, { "name": "3連複", "result": "3-5-10", "price": "6330" }, { "name": "馬連", "result": "5-10", "price": "1330" }, { "name": "3連単", "result": "5-10-3", "price": "26220" } ] }, { "raceNo": "3", "name": " 2歳 未勝利 [指定]", "uma": [ { "no": "1", "number": "12" }, { "no": "2", "number": "10" }, { "no": "3", "number": "5" } ], "wak": [ { "no": "1", "number": "8" }, { "no": "2", "number": "7" }, { "no": "3", "number": "4" } ], "result": [ { "name": "単勝", "result": "12", "price": "180" }, { "name": "馬単", "result": "12-10", "price": "430" }, { "name": "複勝", "result": "12\n10\n5", "price": "110\n130\n140" }, { "name": "ワイド", "result": "10-12\n5-12\n5-10", "price": "160\n270\n360" }, { "name": "枠連", "result": "7-8", "price": "300" }, { "name": "3連複", "result": "5-10-12", "price": "610" }, { "name": "馬連", "result": "10-12", "price": "290" }, { "name": "3連単", "result": "12-10-5", "price": "1560" } ] }, { "raceNo": "4", "name": " メイクデビュー東京", "uma": [ { "no": "1", "number": "5" }, { "no": "2", "number": "7" }, { "no": "3", "number": "13" } ], "wak": [ { "no": "1", "number": "3" }, { "no": "2", "number": "4" }, { "no": "3", "number": "7" } ], "result": [ { "name": "単勝", "result": "5", "price": "250" }, { "name": "馬単", "result": "5-7", "price": "5830" }, { "name": "複勝", "result": "5\n7\n13", "price": "150\n790\n1380" }, { "name": "ワイド", "result": "5-7\n5-13\n7-13", "price": "1480\n4220\n19360" }, { "name": "枠連", "result": "3-4", "price": "4030" }, { "name": "3連複", "result": "5-7-13", "price": "98730" }, { "name": "馬連", "result": "5-7", "price": "4580" }, { "name": "3連単", "result": "5-7-13", "price": "298120" } ] }, { "raceNo": "5", "name": " メイクデビュー東京", "uma": [ { "no": "1", "number": "1" }, { "no": "2", "number": "3" }, { "no": "3", "number": "11" } ], "wak": [ { "no": "1", "number": "1" }, { "no": "2", "number": "3" }, { "no": "3", "number": "8" } ], "result": [ { "name": "単勝", "result": "1", "price": "190" }, { "name": "馬単", "result": "1-3", "price": "630" }, { "name": "複勝", "result": "1\n3\n11", "price": "110\n130\n240" }, { "name": "ワイド", "result": "1-3\n1-11\n3-11", "price": "230\n570\n910" }, { "name": "枠連", "result": "1-3", "price": "390" }, { "name": "3連複", "result": "1-3-11", "price": "1980" }, { "name": "馬連", "result": "1-3", "price": "450" }, { "name": "3連単", "result": "1-3-11", "price": "4780" } ] }, { "raceNo": "6", "name": " 3歳以上 500万下 [指定]", "uma": [ { "no": "1", "number": "3" }, { "no": "2", "number": "9" }, { "no": "3", "number": "8" } ], "wak": [ { "no": "1", "number": "3" }, { "no": "2", "number": "7" }, { "no": "3", "number": "6" } ], "result": [ { "name": "単勝", "result": "3", "price": "560" }, { "name": "馬単", "result": "3-9", "price": "2510" }, { "name": "複勝", "result": "3\n9\n8", "price": "170\n150\n130" }, { "name": "ワイド", "result": "3-9\n3-8\n8-9", "price": "500\n400\n350" }, { "name": "枠連", "result": "3-7", "price": "1450" }, { "name": "3連複", "result": "3-8-9", "price": "1220" }, { "name": "馬連", "result": "3-9", "price": "1420" }, { "name": "3連単", "result": "3-9-8", "price": "9080" } ] }, { "raceNo": "7", "name": " 3歳以上 500万下 [指定]", "uma": [ { "no": "1", "number": "11" }, { "no": "2", "number": "7" }, { "no": "3", "number": "12" } ], "wak": [ { "no": "1", "number": "7" }, { "no": "2", "number": "5" }, { "no": "3", "number": "7" } ], "result": [ { "name": "単勝", "result": "11", "price": "3980" }, { "name": "馬単", "result": "11-7", "price": "31250" }, { "name": "複勝", "result": "11\n7\n12", "price": "660\n230\n140" }, { "name": "ワイド", "result": "7-11\n11-12\n7-12", "price": "3990\n1780\n550" }, { "name": "枠連", "result": "5-7", "price": "920" }, { "name": "3連複", "result": "7-11-12", "price": "14510" }, { "name": "馬連", "result": "7-11", "price": "14780" }, { "name": "3連単", "result": "11-7-12", "price": "160870" } ] }, { "raceNo": "8", "name": " 3歳以上 1000万下 (混合)(特指)", "uma": [ { "no": "1", "number": "4" }, { "no": "2", "number": "6" }, { "no": "3", "number": "5" } ], "wak": [ { "no": "1", "number": "4" }, { "no": "2", "number": "6" }, { "no": "3", "number": "5" } ], "result": [ { "name": "単勝", "result": "4", "price": "380" }, { "name": "馬単", "result": "4-6", "price": "1820" }, { "name": "複勝", "result": "4\n6\n5", "price": "150\n160\n180" }, { "name": "ワイド", "result": "4-6\n4-5\n5-6", "price": "350\n620\n450" }, { "name": "枠連", "result": "4-6", "price": "770" }, { "name": "3連複", "result": "4-5-6", "price": "1600" }, { "name": "馬連", "result": "4-6", "price": "840" }, { "name": "3連単", "result": "4-6-5", "price": "8460" } ] }, { "raceNo": "9", "name": " アイビーステークス", "uma": [ { "no": "1", "number": "4" }, { "no": "2", "number": "6" }, { "no": "3", "number": "5" } ], "wak": [ { "no": "1", "number": "4" }, { "no": "2", "number": "6" }, { "no": "3", "number": "5" } ], "result": [ { "name": "単勝", "result": "4", "price": "6480" }, { "name": "馬単", "result": "4-6", "price": "33420" }, { "name": "複勝", "result": "4\n6", "price": "1950\n700" }, { "name": "ワイド", "result": "4-6\n4-5\n5-6", "price": "1990\n1130\n460" }, { "name": "枠連", "result": " ", "price": "0" }, { "name": "3連複", "result": "4-5-6", "price": "5990" }, { "name": "馬連", "result": "4-6", "price": "12610" }, { "name": "3連単", "result": "4-6-5", "price": "90800" } ] }, { "raceNo": "10", "name": " 秋嶺ステークス", "uma": [ { "no": "1", "number": "7" }, { "no": "2", "number": "2" }, { "no": "3", "number": "10" } ], "wak": [ { "no": "1", "number": "4" }, { "no": "2", "number": "1" }, { "no": "3", "number": "5" } ], "result": [ { "name": "単勝", "result": "7", "price": "230" }, { "name": "馬単", "result": "7-2", "price": "20260" }, { "name": "複勝", "result": "7\n2\n10", "price": "140\n1820\n160" }, { "name": "ワイド", "result": "2-7\n7-10\n2-10", "price": "4740\n290\n6650" }, { "name": "枠連", "result": "1-4", "price": "1700" }, { "name": "3連複", "result": "2-7-10", "price": "19840" }, { "name": "馬連", "result": "2-7", "price": "17680" }, { "name": "3連単", "result": "7-2-10", "price": "121230" } ] }, { "raceNo": "11", "name": " 富士ステークス(GIII)", "uma": [ { "no": "1", "number": "6" }, { "no": "2", "number": "15" }, { "no": "3", "number": "13" } ], "wak": [ { "no": "1", "number": "4" }, { "no": "2", "number": "8" }, { "no": "3", "number": "7" } ], "result": [ { "name": "単勝", "result": "6", "price": "390" }, { "name": "馬単", "result": "6-15", "price": "2120" }, { "name": "複勝", "result": "6\n15\n13", "price": "170\n200\n780" }, { "name": "ワイド", "result": "6-15\n6-13\n13-15", "price": "450\n2250\n2850" }, { "name": "枠連", "result": "4-8", "price": "1000" }, { "name": "3連複", "result": "6-13-15", "price": "12820" }, { "name": "馬連", "result": "6-15", "price": "1080" }, { "name": "3連単", "result": "6-15-13", "price": "50980" } ] }, { "raceNo": "12", "name": " 3歳以上 1000万下 (混合)[指定]", "uma": [ { "no": "1", "number": "10" }, { "no": "2", "number": "3" }, { "no": "3", "number": "9" } ], "wak": [ { "no": "1", "number": "5" }, { "no": "2", "number": "2" }, { "no": "3", "number": "5" } ], "result": [ { "name": "単勝", "result": "10", "price": "9920" }, { "name": "馬単", "result": "10-3", "price": "51770" }, { "name": "複勝", "result": "10\n3\n9", "price": "1790\n180\n910" }, { "name": "ワイド", "result": "3-10\n9-10\n3-9", "price": "5900\n16400\n4220" }, { "name": "枠連", "result": "2-5", "price": "5260" }, { "name": "3連複", "result": "3-9-10", "price": "240430" }, { "name": "馬連", "result": "3-10", "price": "16730" }, { "name": "3連単", "result": "10-3-9", "price": "2780450" } ] } ] } JSON形式で、"{main:{} , data:{}}"という形式でレース毎に配列にしています。 これをさらに集計システムなどを作り込むことで、データを扱うことが可能になります。 また、クロールシステムなどと連携したら、毎日自動でレースデータが取得できると思います。 改めて競馬はデータという情報を分析する非常に数学的なエンターテイメントだと考えさせられました。 お金儲けができるかどうかはアナタ次第です。

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。