地方競馬のレース結果ぶっこ抜きJS [2019年2月版]

2019年2月27日

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

競馬サイトはサイト更新の頻度が結構早いという事がわかりました。 おかげで、前回作ったクローリングプログラムがもはやゴミと化してしましました。 ただ、システム運用をしていると、サイト変更の度に、さらなるクローリングシステムを構築していかなければいけません。 とりあえず、今現在2019年2月現時点の地方競馬サイトのレース結果をクローリングするJSプログラムを掲載しておきます。 ちなみに、以前書いたソースコードはもはやゴミなので、こちらのページの焼き直しだと思ってください。 競馬サイトから結果情報をJSON形式で抜き取るJSコード(地方競馬サイト編)

システム条件

まず、今回は地方競馬サイトの「keiba.go.jp」のクローリングですが、レース結果ページがダイレクトアクセスできるので、そちらのページ遷移までは省きます。 ちなみに、対象ページは以下のような構成になっています。 http://www.keiba.go.jp/KeibaWeb/TodayRaceInfo/RefundMoneyList?k_raceDate=%年/%月/%日&k_babaCode=%馬場コード ある程度の情報を入力すれば、直リンクができるのですが、仕様は下記のようになっています。
%年 : 対象年(西暦) %月 : 対象月(2桁必須) %日 : 対象日(2桁必須) %馬場コード : 下記の馬場コード表を参考にしてください。
札幌 : 8 中京 : 25 帯広 : 3 門別 : 36 盛岡 : 10 水沢 : 11 浦和 : 18 大井 : 20 川崎 : 2 船橋 : 19 名古屋 : 24 笠松 : 23 金沢 : 22 園田 : 27 姫路 : 28 高知 : 31 佐賀 : 32

ソース

;(function(){ var $$ = function(){ return $$.prototype.page2result(); }; // [console-tool] 競馬結果サイトからデータを引き抜き、JSON形式でprintする $$.prototype.page2result = function(){ // location-info (date-location) var main = $$.prototype.getMainData(); // extra date [races] var races = document.querySelectorAll(".refundTable .roundWrapper"); var data = []; for(var i=0; i<races.length; i++){ var raceNo = $$.prototype.getRaceNo(races[i]); var raceData = $$.prototype.getRaceData(races[i]); if(raceNo === null){continue;} data.push({ raceNo : raceNo, result : raceData }); } var full = {"main":main , "data":data}; console.log(JSON.stringify(full,null," ")); return JSON.stringify(full,null," "); }; // レース見出し取得 $$.prototype.getMainData = function(){ var main = {}; var elm = document.querySelector(".refundTable .refund"); var txt = elm.innerText; var ptn = new RegExp("([0-9]{4})年([0-9]{1,2})月([0-9]{1,2})日"); if(txt.match(ptn)){ main.y = RegExp.$1; // 年 main.m = RegExp.$2; // 月 main.d = RegExp.$3; // 日 } var ptn = new RegExp(" (.+)競馬場"); if(txt.match(ptn)){ main.p = RegExp.$1; // 場所 } return main; }; // レース結果情報の取得 $$.prototype.getRaceNo = function(elm){ var raceNo = elm.querySelector("h4.clearfix .roundNum"); if(raceNo){ return raceNo.innerText.replace("R",""); } else{ return null; } }; $$.prototype.getRaceData = function(elm){ var tickets = elm.querySelectorAll(".priceArea .ticket"); var data = []; var ticketName = ""; var results = []; var prices = []; for(var i=0; i<tickets.length; i++){ var result = tickets[i].parentNode.querySelector(".b").innerText; results.push(result); var price = tickets[i].parentNode.querySelector(".c").innerText; price = price.replace("円",""); price = price.replace(/,/g,""); prices.push(price); if(tickets[i].innerText){ ticketName = tickets[i].innerText; } if(tickets[i].parentNode.getAttribute("class") === "border"){ data.push({ name : ticketName, result : results.join("\n"), price : prices.join("\n") }); // clear ticketName = ""; results = []; prices = []; } } return data; }; $$.prototype.txt2numStr = function(str){ var str1 = str; str1 = str1.split("円").join(""); str1 = str1.split(",").join(""); return str1; }; console.log($$()); return $$; })(); { "main": { "y": "2019", "m": "2", "d": "24", "p": "帯広ば" }, "data": [ { "raceNo": "1", "result": [ { "name": "単勝", "result": "2", "price": "440" }, { "name": "複勝", "result": "2\n4\n8", "price": "220\n210\n210" }, { "name": "馬連複", "result": "2-4", "price": "1330" }, { "name": "馬連単", "result": "2-4", "price": "2050" }, { "name": "ワイド", "result": "2-4\n2-8\n4-8", "price": "520\n850\n550" }, { "name": "三連複", "result": "2-4-8", "price": "6440" }, { "name": "三連単", "result": "2-4-8", "price": "30780" } ] }, { "raceNo": "2", "result": [ { "name": "単勝", "result": "8", "price": "380" }, { "name": "複勝", "result": "8\n7\n9", "price": "220\n400\n320" }, { "name": "枠連複", "result": "7-8", "price": "1550" }, { "name": "馬連複", "result": "7-8", "price": "1580" }, { "name": "馬連単", "result": "8-7", "price": "4500" }, { "name": "ワイド", "result": "7-8\n8-9\n7-9", "price": "550\n570\n1000" }, { "name": "三連複", "result": "7-8-9", "price": "3260" }, { "name": "三連単", "result": "8-7-9", "price": "14490" } ] }, { "raceNo": "3", "result": [ { "name": "単勝", "result": "9", "price": "860" }, { "name": "複勝", "result": "9\n7\n6", "price": "140\n130\n110" }, { "name": "枠連複", "result": "7-8", "price": "710" }, { "name": "馬連複", "result": "7-9", "price": "760" }, { "name": "馬連単", "result": "9-7", "price": "1250" }, { "name": "ワイド", "result": "7-9\n6-9\n6-7", "price": "270\n190\n140" }, { "name": "三連複", "result": "6-7-9", "price": "440" }, { "name": "三連単", "result": "9-7-6", "price": "2750" } ] }, { "raceNo": "4", "result": [ { "name": "単勝", "result": "3", "price": "2590" }, { "name": "複勝", "result": "3\n8\n6", "price": "390\n290\n180" }, { "name": "枠連複", "result": "3-8", "price": "5120" }, { "name": "馬連複", "result": "3-8", "price": "19210" }, { "name": "馬連単", "result": "3-8", "price": "37610" }, { "name": "ワイド", "result": "3-8\n3-6\n6-8", "price": "4740\n870\n920" }, { "name": "三連複", "result": "3-6-8", "price": "10830" }, { "name": "三連単", "result": "3-8-6", "price": "97960" } ] }, { "raceNo": "5", "result": [ { "name": "単勝", "result": "5", "price": "720" }, { "name": "複勝", "result": "5\n3\n7", "price": "150\n110\n150" }, { "name": "馬連複", "result": "3-5", "price": "950" }, { "name": "馬連単", "result": "5-3", "price": "2050" }, { "name": "ワイド", "result": "3-5\n5-7\n3-7", "price": "390\n550\n350" }, { "name": "三連複", "result": "3-5-7", "price": "1290" }, { "name": "三連単", "result": "5-3-7", "price": "9750" } ] }, { "raceNo": "6", "result": [ { "name": "単勝", "result": "5", "price": "940" }, { "name": "複勝", "result": "5\n4\n3", "price": "280\n150\n850" }, { "name": "枠連複", "result": "4-5", "price": "470" }, { "name": "馬連複", "result": "4-5", "price": "790" }, { "name": "馬連単", "result": "5-4", "price": "2280" }, { "name": "ワイド", "result": "4-5\n3-5\n3-4", "price": "660\n2640\n1100" }, { "name": "三連複", "result": "3-4-5", "price": "8680" }, { "name": "三連単", "result": "5-4-3", "price": "52110" } ] }, { "raceNo": "7", "result": [ { "name": "単勝", "result": "9", "price": "240" }, { "name": "複勝", "result": "9\n3\n4", "price": "130\n520\n750" }, { "name": "枠連複", "result": "3-8", "price": "2290" }, { "name": "馬連複", "result": "3-9", "price": "1690" }, { "name": "馬連単", "result": "9-3", "price": "3440" }, { "name": "ワイド", "result": "3-9\n4-9\n3-4", "price": "850\n1280\n2970" }, { "name": "三連複", "result": "3-4-9", "price": "7230" }, { "name": "三連単", "result": "9-3-4", "price": "31210" } ] }, { "raceNo": "8", "result": [ { "name": "単勝", "result": "9", "price": "510" }, { "name": "複勝", "result": "9\n2\n1", "price": "240\n150\n230" }, { "name": "枠連複", "result": "2-8", "price": "860" }, { "name": "馬連複", "result": "2-9", "price": "950" }, { "name": "馬連単", "result": "9-2", "price": "2250" }, { "name": "ワイド", "result": "2-9\n1-9\n1-2", "price": "310\n1060\n680" }, { "name": "三連複", "result": "1-2-9", "price": "3640" }, { "name": "三連単", "result": "9-2-1", "price": "19150" } ] }, { "raceNo": "9", "result": [ { "name": "単勝", "result": "1", "price": "290" }, { "name": "複勝", "result": "1\n3\n9", "price": "140\n130\n120" }, { "name": "枠連複", "result": "1-3", "price": "930" }, { "name": "馬連複", "result": "1-3", "price": "950" }, { "name": "馬連単", "result": "1-3", "price": "2110" }, { "name": "ワイド", "result": "1-3\n1-9\n3-9", "price": "250\n200\n170" }, { "name": "三連複", "result": "1-3-9", "price": "380" }, { "name": "三連単", "result": "1-3-9", "price": "4130" } ] }, { "raceNo": "10", "result": [ { "name": "単勝", "result": "3", "price": "630" }, { "name": "複勝", "result": "3\n1\n6", "price": "160\n120\n270" }, { "name": "枠連複", "result": "1-3", "price": "630" }, { "name": "馬連複", "result": "1-3", "price": "690" }, { "name": "馬連単", "result": "3-1", "price": "1600" }, { "name": "ワイド", "result": "1-3\n3-6\n1-6", "price": "220\n480\n430" }, { "name": "三連複", "result": "1-3-6", "price": "1570" }, { "name": "三連単", "result": "3-1-6", "price": "8920" } ] }, { "raceNo": "11", "result": [ { "name": "単勝", "result": "1", "price": "240" }, { "name": "複勝", "result": "1\n4\n2", "price": "150\n270\n570" }, { "name": "枠連複", "result": "1-4", "price": "2630" }, { "name": "馬連複", "result": "1-4", "price": "1840" }, { "name": "馬連単", "result": "1-4", "price": "2730" }, { "name": "ワイド", "result": "1-4\n1-2\n2-4", "price": "490\n1380\n3830" }, { "name": "三連複", "result": "1-2-4", "price": "8070" }, { "name": "三連単", "result": "1-4-2", "price": "47690" } ] } ] }

ちょこっと解説

このスクレイピングでめんどくさかった点は、払い戻し情報の取得の箇所ですが、テーブル2つの構成で、「複勝」と「ワイド」が複数行になっているため、これらのために、配列にいれて、「\n」でjoinするようにしています。 joinの文字列は正直なんでもいいのですが、その後につなげるシステムに見合う文字に改造してもらっても構いません。 少し心配なのが、同着やイレギュラーの表示が確認できていないので、その際はまた手を入れなければいけませんが、今の所わからないので、このままにしておきます。 実サイトクローリングのデメリットは、ページ改修に弱いという点ですが、こうしたプログラムを効率的に更新できる仕組みを作っておくことが重要なのかもしれませんね。 ちなみに、今回のプログラム構築作業は3時間ほどでした。

このブログを検索

ごあいさつ

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