🏇

Seleniumを用いた馬券購入プログラムの作成

2021/01/25に公開
6

モチベーション

昨今ではPythonをはじめとしたさまざまなプログラミング言語において機械学習ライブラリが普及しており,ライブラリを用いれば誰でも簡単に機械学習を行えるようになりました.今回はその機械学習を用いた競馬の結果予測に関する記事となります.

機械学習では,結果をモデルを構築するために特徴量の最適化やハイパーパラメータの調整などさまざまな工夫が必要であるため,多くの時間を要します.そのような中,機械学習とは直接関係がない馬券購入の自動化プログラムの作成などはあまり時間をかけたくないかと思われます.馬券購入プログラムは専用アプリケーションやVBAによる実装などが存在するとはいえ,機械学習で作成したプログラムとの親和性が高いとは言えず,機械学習による馬番予測から馬券購入までの流れを全自動化することは容易ではありません.

今回は機械学習の実装を容易に行えるPythonで,Seleniumとよばれるライブラリを用いてJRA IPATから馬券を購入するプログラムを作成します.これにより,みなさまの機械学習を用いた競馬予測において精度向上に注力できる環境を提供することに加えて,馬券購入までの一連の流れを自動化する足掛かりとなることを期待します.

※ なお,本記事で取り扱っているプログラムはJRA IPATの本番環境でのプログラムであるため,馬券購入可能期間以外ではプログラムが正常に動作しません.

SeleniumおよびWebドライバのインストール

本記事ではSeleniumとよばれるPythonのライブラリを使用して馬券購入プログラムを作成します.Seleniumを用いることでプログラミングによりWebブラウザを自動制御できます.SeleniumによるWebブラウザの自動制御には,Selenium本体に加えて使用するWebブラウザに応じたWebドライバの導入が必要です.以下のコマンドを実行することでSeleniumおよびWebドライバを管理するWebドライバマネージャをインストールします.

pip install selenium webdriver_manager

通常,使用するWebブラウザによって適切なWebドライバをインストールする必要がありますが,webdriver_managerを用いることでこれを容易に実現できます.
https://pypi.org/project/webdriver-manager/

今回はWebブラウザとしてGoogle Chromeを用いる場合を想定してSeleniumを動作させます.

馬券購入プログラムの実装

JRA IPATから馬券を購入するためには以下の流れでプログラムを作成する必要があります.

そこで本記事では上記の2本立てで説明を行います.今回,プログラムを機能毎に実装するため,事前にインポートするライブラリや全体を通して使用するグローバル変数・自作関数を以下に記載しておきます.IDやパスワード類,入金金額や馬券購入枚数などについては各自で適宜変更が必要です.またlogin_jra_pat()はJRA IPATへログインするための関数ですが,本記事ではSeleniumによるブラウジングの動作を確認するため,options.add_argument("-headless")をコメントアウトしています.本番環境などにおいてSeleniumをバックグラウンドで動作させたい場合などは適宜コメントアウトを外してください.

# インポートするライブラリ
import datetime
from time import sleep

from selenium import webdriver
from selenium.webdriver.common.alert import Alert
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager


# グローバル変数
# 曜日リスト
dow_lst = ["月", "火", "水", "木", "金", "土", "日"]
# レース会場のリスト
place_lst = ["札幌", "函館", "福島", "新潟", "東京", "中山", "中京", "京都", "阪神", "小倉"]

# JRA IPATのurl
pat_url = "https://www.ipat.jra.go.jp/index.cgi"

# INETID
inet_id = ""
# 加入者番号
kanyusha_no = ""
# PATのパスワード
password_pat = ""
# P-RAS番号
pras_no = ""

# JRA IPATへの入金金額[yen]
deposit_money = 10000

# 馬券の購入枚数
ticket_nm = 10

# seleniumの待機時間[sec]
wait_sec = 2


# 自作関数
def judge_day_of_week(date_nm):

    date_dt = datetime.datetime.strptime(str(date_nm), "%Y%m%d")

    # 曜日を数字で返す(月曜:1 〜 日曜:7)
    nm = date_dt.isoweekday()
    return dow_lst[nm - 1]


def click_css_selector(driver, selector, nm):

    el = driver.find_elements_by_css_selector(selector)[nm]
    driver.execute_script("arguments[0].click();", el)
    sleep(wait_sec)


def login_jra_pat():

    options = Options()
    # options.add_argument("-headless")
    driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)
    driver.get(pat_url)

    # PAT購入画面に遷移・ログイン
    # INETIDを入力する
    driver.find_elements_by_css_selector("input[name^='inetid']")[0].send_keys(inet_id)
    click_css_selector(driver, "a[onclick^='javascript']", 0)
    sleep(wait_sec)

    # 加入者番号,PATのパスワード,P-RAS番号を入力する
    driver.find_elements_by_css_selector("input[name^='p']")[0].send_keys(password_pat)
    driver.find_elements_by_css_selector("input[name^='i']")[2].send_keys(kanyusha_no)
    driver.find_elements_by_css_selector("input[name^='r']")[1].send_keys(pras_no)
    click_css_selector(driver, "a[onclick^='JavaScript']", 0)

    # お知らせがある場合はOKを押す
    if "announce" in driver.current_url:
        click_css_selector(driver, "button[href^='#!/']", 0)

    return driver

JRA IPATへの入金処理

JRA IPATへ入金するために以下のようにプログラムを実装します.本記事では入金処理と馬券購入処理を独立して実行できるように入金時・馬券購入時それぞれでログイン処理を挟むように実装しています.馬券購入前に毎回入金処理を行いたい場合などは適宜変更してください.また,入金処理直前にAlert(driver).accept()を実行していますが,この部分をAlert(driver).dismiss()とすることで入金処理を中断することも可能です.

def deposit():

    driver = login_jra_pat()

    # 入出金ページに遷移する(新しいタブに遷移する)
    click_css_selector(driver, "button[ng-click^='vm.clickPayment()']", 0)
    driver.switch_to.window(driver.window_handles[1])

    # 入金指示を行う
    click_css_selector(driver, "a[onclick^='javascript'", 1)
    driver.find_elements_by_css_selector("input[name^='NYUKIN']")[0].send_keys(deposit_money)
    click_css_selector(driver, "a[onclick^='javascript'", 1)
    driver.find_elements_by_css_selector("input[name^='PASS_WORD']")[0].send_keys(password_pat)
    click_css_selector(driver, "a[onclick^='javascript'", 1)

    # 確認事項を承諾する
    Alert(driver).accept()

    sleep(wait_sec)
    driver.close()
    driver.quit()

馬券の購入処理

レース結果予測後に馬券を購入するためには以下の情報が必要になります.

  • レース会場
  • 曜日
  • レース番号
  • 馬番

これらの情報は馬券購入時に限らずレースの結果予測においても必要であるかと思われます.本記事ではnetkeiba.comのレースIDを参考にしてレース会場・レース番号を取得します.netkeiba.comでは12桁の数字でレースIDを管理しています.例えばレースIDが202006050701の場合,2020年(2020) 中山(06) 5回(05) 7日目(07) 1R(01)のように構成されています.そこで以下のようにレースID,年月日および予測した馬番を引数としてプログラムを実装します.またプログラム内でclick_css_selector(driver, "button[ng-click^='vm.dismiss()']", 1)を実行していますが,この部分が馬券購入時の最終確認であるため,ここをコメントアウトすることでプログラムの検証を行うことが可能です.

def buy_jra_pat(race_id, date_nm, h_nm):

    driver = login_jra_pat()

    # 購入処理開始
    # 通常投票を指定する
    click_css_selector(driver, "button[href^='#!/bet/basic']", 0)

    # レース会場を指定する
    place = place_lst[int(race_id[4:6]) - 1]
    dow = judge_day_of_week(date_nm)
    lst = driver.find_elements_by_css_selector("button[ng-click^='vm.selectCourse(oCourse.courseId)']")
    for el in lst:
        if (place in el.text) & (dow in el.text):
            driver.execute_script("arguments[0].click();", el)
            sleep(wait_sec)
            break

    # レース番号を指定する
    race_nm = int(race_id[10:12])
    lst = driver.find_elements_by_css_selector("button[ng-click^='vm.selectRace(oJgRn.nRaceIndex + 1)']")
    for el in lst:
        if str(race_nm) in el.text:
            driver.execute_script("arguments[0].click();", el)
            sleep(wait_sec)
            break

    # 購入する馬番をクリックし,購入金額を指定する
    click_css_selector(driver, "label[for^=no{}]".format(h_nm), 0)
    driver.find_element_by_css_selector("input[ng-model^='vm.nUnit']").send_keys(ticket_nm)

    # 購入用変数をセットする
    click_css_selector(driver, "button[ng-click^='vm.onSet()']", 0)
    click_css_selector(driver, "button[ng-click^='vm.onShowBetList()']", 0)

    # 購入する
    money = driver.find_element_by_css_selector("span[ng-bind^='vm.getCalcTotalAmount() | number']").text
    driver.find_element_by_css_selector("input[ng-model^='vm.cAmountTotal']").send_keys(money)
    click_css_selector(driver, "button[ng-click^='vm.clickPurchase()']", 0)

    # 購入処理を完了させる
    click_css_selector(driver, "button[ng-click^='vm.dismiss()']", 1)

    sleep(wait_sec)
    driver.close()
    driver.quit()

上記により,JRA IPATへの入金処理・馬券購入処理を実行することができます.

Discussion

ke1yuke1yu

はじめまして、けいゆうと申します。
馬券購入プログラムの情報は少ないので大変助かりました! 本当にありがとうございます。
さて、こちらのコードを改造した複数馬券対応のプログラムを使っているのですが、それをブログ記事にしたいと考えておりコメントいたしました。本記事と共に紹介しても構わないでしょうか。
ご確認よろしくお願いします。

_lambda314_lambda314

はじめまして、ご丁寧にありがとうございます。本記事が役に立ったとのことで大変うれしい限りです。本記事の内容をブログ記事にご活用したいとのこと、承知しました。是非よろしくお願いします!

ke1yuke1yu

わあ読んでいただけるなんて! ありがとうございます!