競馬予想AIの作り方 〜 Rで出馬表をWebスクレイピング

R

前回の記事でせっかくスピード指数をスクレイピングしてデータベースを作ったのだから、次のレースの検討に活かしたい‼️そうすれば、きっと…(笑)。というわけで、今週末開催されるレースの出走テーブルをnetkeibaさんからスクレイピングさせてもらうことにしました。

スピード指数のスクレイピングについてはこちら

競馬予想AIの作り方 〜 Rでスピード指数をスクレイピング
スピード指数データは「無料」「スピード指数」で検索して、トップに出てくる下記のサイトからを入手させていただくことにしました。 競馬新聞&スピード指数(無料)

出走テーブルのスクレイピング

スクレイピングを始める前に

Rでスクレイピングをするならrvestパッケージを使うのが簡単です。また、スクレイピングをするためにはHTML/CSSの理解も必要。とりあえず、これだけ知っていればスクレイピングは始められます。

Rでスクレイピングするならrvest 表もリンクもテキストも
Rでスクレイピングをするならrvestパッケージを使うのがベスト。表データ、リンク先URL、テキストなどのデータを簡単に入手できます。netkeibaのレース結果を題材にrvestパッケージの使い方をまとめています。
スクレイピングに必要な最低限のHTML/CSSの知識
スクレイピングでデータを収集するためには、HTMLで書かれたWebページの文書構造を理解し、どこに目的のデータが記載されているかをコンピュータに教えてやる必要があります。このページではスクレイピングに最低限必要なHTML/CSSの知識をまとめています。

 

コードの概要

出走テーブルの入手は以下のようなステップで行います。

  1. 出走テーブルを入手したい日付の設定
  2. 指定した日付のrace_idを取得
  3. 入手したrace_idに対応するページから出走テーブルページのhtmlデータを読み込む
  4. htmlデータから各馬共通の条件を抽出
  5. htmlデータから馬ごとのデータを抽出
  6. 4,5のデータを合体
  7. 入手したrace_idの個数分、3〜6を繰り返す

race_idの取得

入力値 :
race_date : 出走テーブルを取得したい日付。yyyymmdd形式。

戻り値:
指定した日に開催されるレースのrace_id。このrace_idでhtmlページ等は識別されている。

get_race_ids <- function(race_date){

    #netkeiba開催日のページ
    url_base <- "https://race.netkeiba.com/top/race_list_sub.html?kaisai_date="
    url <- paste0(url_base, race_date)

    #race_idの抽出
    race_ids <- read_html(url, encoding = "UTF-8") %>%
        html_nodes("a") %>%
        html_attr("href") %>%
        str_subset(".*race_id=") %>%
        str_replace(".*race_id=", "") %>%
        str_replace("&rf=.*", "") %>%
        unique()

    cat(" Sleep")
    Sys.sleep(1)
    cat(" Done\n")

    race_ids
}

出走テーブルのページの読み込み

入力値: race_id

戻り値: race_idのページに対応するhtmlデータ

get_sheduled_race_html <- function(race_id){

    url_base <- "https://race.netkeiba.com/race/shutuba.html?race_id="
    url <- paste0(url_base, race_id)

    html <- read_html(url, encoding = "EUC-JP")

    cat(" Sleep")
    Sys.sleep(1)
    cat(" Done\n")

    html
}

各馬共通の条件を抽出

入力値:
html: htmlデータ
race_id: race_id

戻り値:
全馬共通のレース情報(data.frame型)

get_sheduled_race_cond <- function(html, race_id){

    #レース
    race_No <- html %>%
        html_element(".RaceNum") %>%
        html_text() %>%
        str_remove("R") %>%
        as.integer()

    #レース名
    race_name <- html %>%
        html_element(".RaceName") %>%
        html_text() %>%
        str_trim()

    race_data01 <- html %>%
        html_element(".RaceData01") %>%
        html_text() %>%
        str_remove_all("\n")

    race_data02 <- html %>%
        html_element(".RaceData02") %>%
        html_text() %>%
        str_split("\n") %>%
        unlist()

    # 開催
    kaisai <- paste0(race_data02[2:4], collapse = "")

    #競馬場
    place <- race_data02[3]

    #クラス
    class <- paste0(race_data02[5:6], collapse = "")

    #馬場
    turf <- race_data01 %>%
        str_split("/") %>%
        .[[1]] %>%
        .[2] %>%
        str_trim() %>%
        str_sub(1,1)

    # 距離
    distance <- race_data01 %>%
        str_split("/") %>%
        .[[1]] %>%
        .[2] %>%
        str_extract("\\d+") %>%
        as.integer()

    #左右
    rotation <- race_data01 %>%
        str_split("/") %>%
        .[[1]] %>%
        .[2] %>%
        str_replace("^.*\\((.)\\)$", "\\1")

    #戻り値用のdata.frameの準備
    df.cond <- tibble("race_id" = race_id,
        "開催" = kaisai,
        "競馬場" = place,
        "レース" = race_No,
        "レース名" = race_name,
        "クラス" = class,
        "馬場" = turf,
        "距離" = distance,
        "左右" = rotation)

    df.cond
}

馬ごとのデータの抽出

入力値:
html: htmlデータ
race_id: race_id

戻り値:
馬ごとのレース情報(data.frame型)

get_sheduled_race_horse <- function(html, race_id){

    # 馬ごとのデータ(出馬表)
    df.horse <- html %>%
        html_element(".Shutuba_Table") %>%
        html_table()

    # data.frameの調整
    names(df.horse) <- unlist(df.horse[1, ])
    df.horse <- df.horse[-1, ]

    df.horse <- df.horse %>%
        select(`枠`, `馬番`, `馬名`, `性齢`, `斤量`, `騎手`, `厩舎`) %>%
        mutate(race_id = race_id, .before = everything())

    df.horse
}

出走テーブル取得関数(ある開催日のデータ取得)

入力値:
race_date: データを取得したい開催日

戻り値:
race_dateに対応する出走テーブル情報(data.frame型)

get_sheduled_race_table <- function(race_date){

    # race_idの取得
    race_ids <- get_race_ids(race_date)

    #    
    df.cond <- NULL
    df.horse <- NULL

    for(race_id in race_ids){

    # htmlデータの読み込み
        html <- get_sheduled_race_html(race_id)

    # 全馬共通条件の処理
        df.cond.tmp <- get_sheduled_race_cond(html, race_id)

    # 馬ごとのデータの処理
        df.horse.tmp <- get_sheduled_race_horse(html, race_id)

    # data.frameの合体
        if(is.null(df.cond)){
            df.cond <- df.cond.tmp
            df.horse <- df.horse.tmp
        }else{
            df.cond <- rbind(df.cond, df.cond.tmp)
            df.horse <- rbind(df.horse, df.horse.tmp)
        }
    }

  # data.frameの合体
    df.race.table <- left_join(df.cond, df.horse)

    df.race.table
}

オッズ、人気順

ここで紹介したrvestを用いたスクレイピングは軽快に動作するのですが、netkeibaの出馬表データのうち、オッズ、人気順のデータは取得できません。オッズ、人気順を取得するためには、RSeleniumパッケージを利用する必要がありますが、動作は重くなります。活用法に応じて適宜使い分けが必要です。RSeleniumを用いた方法はこちら。

競馬予想AI netkeibaのスクレイピングでオッズ、人気も取得
netkeibaをスクレイピング。でも、rvestパッケージではnetkeibaのオッズや人気は、スクレイピングできないんです。ここでは、RSeleniumパッケージを使ってオッズや人気をスクレイピングする方法を紹介しています。

まとめ

今回のコードで週末に予定されているレースに出走する馬とレースの条件を入手できるようになりました。入手した出走テーブル情報と前回取得したスピード指数をリンクさせることで、より精度の高い検討ができるようになるはず(笑)。

スピード指数のスクレイピングはこちら

競馬予想AIの作り方 〜 Rでスピード指数をスクレイピング
スピード指数データは「無料」「スピード指数」で検索して、トップに出てくる下記のサイトからを入手させていただくことにしました。 競馬新聞&スピード指数(無料)

 

出馬表とスピード指数のスクレイピング結果を組み合わせるとこんなにわかりやすくなります。

競馬予想 データを可視化 スピード指数を活用する
スピード指数を可視化して、出馬表をグラフィカルにするためのソースコードを公開します。データを可視化すれば、情報を適切に評価して、迷うことなく買い目を選択できるようになったり、競馬が上手な人にしかわからないことがわかるようになるなどの効果が得...
今週のメインレース の「血統指数」「タイム指数」「1着予想確率」
競馬予想に大切な要素である血統(父馬、母父馬)、過去の走破タイムを数値で評価。数値化することでこれまで競馬が上手い人にしかわからなかったこともクッキリ。

 

競争結果のスクレイピングはこちら

競馬 過去データcsvをnetkeibaから無料で入手する方法
JRA-VANを無料で使えるのはお試し期間のみ。競馬のデータ分析を継続して行うには、どうしても過去の競馬レース結果データを自前で収集する必要があります。netkeibaからのスクレイピング方法やデータをcsv 形式で保存する方法について記述しています。