【スクレイピング】全賭け方のオッズをnetkeibaから取得

競馬AI

本記事の目的

目的
  • 「netkeiba.com」から全賭け方のオッズを取得する

全賭け方のオッズ

ソースコード解説

それでは早速、ソースコードを交えて解説していきます。

大きな流れとしては、下記のとおりです。

処理全体の流れ
  • ①Google Chromeを起動
  • ②単勝・複勝のオッズを取得
  • ③馬連のオッズを取得
  • ④ワイドのオッズを取得
  • ⑤馬単のオッズを取得
  • ⑥3連複のオッズを取得
  • ⑦3連単のオッズを取得

①Google Chromeを起動

3連複、3連単のオッズは、それぞれ「単勝・複勝」タブや「枠連」タブ等をクリックしないと表示されません。

かつ、それぞれのタブでURLが変化しないため、実際にブラウザでこれらのタブをクリックしてから、オッズデータを取得する必要があります。

そこで、今回はWebブラウザを遠隔操作する「Selenium」というモジュールを使用して、スクレイピングを行います。

今回は「Google Chrome」を遠隔操作するので、まずは「Google Chrome」を起動します。

ここでの注意点としては、下記が挙げられます。

ブラウザとWebDriverのバージョンが異なるとエラーとなります。

この問題を解決するために、本処理では「webdriver_manager」というパッケージを使用します。

このパッケージを使用すると、両者のバージョンが異なる場合は適合するChromeDriverを自動的にダウンロードしてくれます。

    # ライブラリ
    from selenium import webdriver
    from webdriver_manager.chrome import ChromeDriverManager

    # パラメータ
    interval = 30

  # Google Chromeを起動 ---------------------- (※1)
    browser = webdriver.Chrome(ChromeDriverManager().install()) -------------#(1)
    browser.implicitly_wait(interval) ------------- #(2)

②単勝・複勝のオッズを取得

【処理概要】単勝・複勝
  • (1) ページを開く
  • (2) オッズテーブルを作成
  • (3) オッズテーブルを辞書型変数に格納

②-1 ページを開く

全体ソースコード」を見ると分かりますが、コード全体をレースID(変数名:race_id)でループする処理となっているので、URLを変数化しています。

そのURLを使用して、Seleniumuの「.get()」メソッドで指定したURLに遷移します。

            # ページを開く
            url = 'https://race.netkeiba.com/odds/index.html?type=b1&race_id='\
                 + str(race_id) + '&rf=shutuba_submenu'            
            browser.get(url)

②-2 オッズテーブルを作成

オッズテーブルのHTMLを確認すると、<table>タグであることが分かります。

「欲しいデータを選択 → 右クリック → 検証」の順に操作することで、HTMLを確認できます。

<able>タグの表データは、Pandasの「read_html()」メソッドを使用すると簡単に取得できます。

その後、取得したオッズテーブルから「馬番」「オッズ」を抽出し、「レースID」を追加することで、新たなオッズテーブルを作成しています。

            # オッズテーブルを作成
            table_tag_list = browser.find_elements_by_css_selector("table.RaceOdds_HorseList_Table")
            cnt = 0
            for table_tag in table_tag_list:
                html = table_tag.get_attribute('outerHTML')
                if cnt == 0:
                    tansyo_table = pd.read_html(html)[0]
                elif cnt == 1:
                    fukusyo_table = pd.read_html(html)[0]
                cnt += 1
            
            horse_num = len(tansyo_table)
            
            # オッズテーブル
            tansyo_df = pd.DataFrame(data={'horse_num': tansyo_table['馬番'],
                                           'odds': tansyo_table['オッズ'],
                                           'race_ID': [race_id] * horse_num})
            fukusyo_df = pd.DataFrame(data={'horse_num': fukusyo_table['馬番'],
                                            'odds': fukusyo_table['オッズ'],
                                            'race_ID': [race_id] * horse_num})

②-3 辞書型変数に格納

最後に、辞書型変数の値に先ほどのデータフレーム型変数を追加します。(キーはレースIDです。)

            # 辞書型変数に格納
            odds_tansyo[race_id] = tansyo_df
            odds_fukusyo[race_id] = fukusyo_df

以上で、単勝・複勝のオッズを取得する処理が完了しました。

③馬連のオッズを取得

基本的に「②単勝・複勝のオッズを取得」と同じ処理なので、馬連固有の箇所を対象に解説します。

処理概要】馬連
  • (1)「馬連」タブをクリック ※馬連固有
  • (2) オッズテーブルを作成
  • (3) 辞書型変数に格納

③-1 「馬連」タブをクリック

ページ内で「馬連」という文字列を含む要素を取得し、その要素の場所までスクロールします。

画面表示外の要素をクリックしようとするとエラーになるので、スクロールする処理を加えています。

スクロールした後はその要素クリックし、1秒待機します。(サーバへの負荷軽減のためです。)

            # 「馬連」タブをクリック 
            text = browser.find_element_by_partial_link_text('馬連')  
            browser.execute_script('arguments[0].scrollIntoView(true);', text)
            text.click()
            time.sleep(1)

④ワイドのオッズを取得

基本的に「③馬連のオッズを取得」と同じ処理なので、詳細の解説は割愛します。

処理概要】ワイド
  • (1)「ワイド」タブをクリック
  • (2) オッズテーブルを作成
  • (3) 辞書型変数に格納

⑤馬単のオッズを取得

基本的に「③馬連のオッズを取得」と同じ処理なので、詳細の解説は割愛します。

処理概要】馬単
  • (1)「馬単」タブをクリック
  • (2) オッズテーブルを作成
  • (3) 辞書型変数に格納

⑥3連複のオッズを取得

基本的に「③馬連のオッズを取得」と同じ処理なので、3連複固有の箇所を対象に解説します。

処理概要】3連複
  • (1)「馬連」タブをクリック
  • (2) オッズテーブルを作成 3連複固有
  • (3) 辞書型変数に格納

⑥-2 オッズテーブルを作成

3連複はパターンが多いため、オッズ情報を取得するには1頭目の競走馬をプルダウンで選択する必要があります。

なので、全パターンのオッズ情報を取得するにはプルダウンから各競走馬を選択する必要があります。

そこでプルダウンのHTMLを確認すると、

 >ID名が「list_select_horse」

であることが分かります。

本処理では、上記情報をもとにプルダウンをクリックする必要がありますが、以降のオッズテーブルを作成する処理は「②-4 オッズテーブルを作成」と同様の方法で取得します。

            # オッズ情報を個別に取得
            # 基準とする競走馬を選択する
            horse_list = [str(x) + ' ' + str(y)\
                          for x, y in zip(tansyo_table['馬番'], tansyo_table['馬名'])]
                  
            # 初期値
            pattern_list, odds_list = [], []
            cnt = 0
                
            for horse in horse_list:

                # プルダウンを選択
                dropdown = browser.find_element_by_id('list_select_horse')
                Select(dropdown).select_by_visible_text(horse)
                time.sleep(1)

                # オッズテーブルを作成
                table_tag_list = browser.find_elements_by_css_selector("table.Odds_Table")
                for table_tag in table_tag_list:
                    html = table_tag.get_attribute('outerHTML')
                    odds_table = pd.read_html(html)[0]
                    # 賭け方のパターン
                    pattern_list += [str(tansyo_table['馬番'].iloc[cnt, ]) + '-'\
                                     + str(odds_table.columns[0]) + '-'\
                                     + str(x)\
                                     for x in odds_table.iloc[:, 0]]
                    # オッズ
                    odds_list += list(odds_table.iloc[:, 1])
                    
                cnt += 1
            
            # オッズテーブル                      
            sanrenpuku_df = pd.DataFrame(data={'pattern': pattern_list,
                                               'odds': odds_list,
                                               'race_ID': [race_id] * len(odds_list)})

⑦3連単のオッズを取得

基本的に「⑥3連複のオッズを取得」と同じ処理なので、詳細の解説は割愛します。

処理概要】3連単
  • (1)「3連単」タブをクリック
  • (2) オッズテーブルを作成
  • (3) 辞書型変数に格納

全体ソースコード

最後に、全体ソースコードを掲載します。

""" オッズ情報をスクレイピング """
# ライブラリ
import urllib.request
from tqdm import tqdm
import time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from webdriver_manager.chrome import ChromeDriverManager
import pandas as pd
import itertools


def scrape_odds_table(race_id_list):
    
    # 初期値
    interval = 30
    odds_tansyo, odds_fukusyo, odds_umaren, odds_umatan, odds_wide = {}, {}, {}, {}, {}
    odds_wide, odds_sanrenpuku, odds_sanrentan = {}, {}, {}
    
    # Google Chromeを起動 ---------------------- (※1)
    browser = webdriver.Chrome(ChromeDriverManager().install())
    browser.implicitly_wait(interval)
    
    # レースID毎にループ
    for race_id in tqdm(race_id_list):
           
        try:
            
            # 1.単勝・複勝
            # ページを開く
            url = 'https://race.netkeiba.com/odds/index.html?type=b1&race_id='\
                 + str(race_id) + '&rf=shutuba_submenu'            
            browser.get(url)
            time.sleep(1)
            
            # オッズテーブルを作成
            table_tag_list = browser.find_elements_by_css_selector("table.RaceOdds_HorseList_Table")
            cnt = 0
            for table_tag in table_tag_list:
                html = table_tag.get_attribute('outerHTML')
                if cnt == 0:
                    tansyo_table = pd.read_html(html)[0]
                elif cnt == 1:
                    fukusyo_table = pd.read_html(html)[0]
                cnt += 1
            
            horse_num = len(tansyo_table)
            
            # オッズテーブル
            tansyo_df = pd.DataFrame(data={'horse_num': tansyo_table['馬番'],
                                           'odds': tansyo_table['オッズ'],
                                           'race_ID': [race_id] * horse_num})
            fukusyo_df = pd.DataFrame(data={'horse_num': fukusyo_table['馬番'],
                                            'odds': fukusyo_table['オッズ'],
                                            'race_ID': [race_id] * horse_num})
                        
            # 辞書型変数に格納
            odds_tansyo[race_id] = tansyo_df
            odds_fukusyo[race_id] = fukusyo_df
            
            
            # 2. 馬連
            # 「馬連」タブをクリック 
            text = browser.find_element_by_partial_link_text('馬連')  
            browser.execute_script('arguments[0].scrollIntoView(true);', text)
            text.click()
            time.sleep(1)
            
            # オッズテーブルを作成
            table_tag_list = browser.find_elements_by_css_selector("table.Odds_Table")
            pattern_list, odds_list = [], []
            for table_tag in table_tag_list:
                html = table_tag.get_attribute('outerHTML')
                umaren_table = pd.read_html(html)[0]
                # 馬連のパターン
                pattern_list += [str(umaren_table.columns[0]) + '-' + str(x)\
                                 for x in umaren_table.iloc[:, 0]]
                # オッズ
                odds_list += list(umaren_table.iloc[:, 1])
                                  
            umaren_df = pd.DataFrame(data={'pattern': pattern_list,
                                           'odds': odds_list,
                                           'race_ID': [race_id] * len(odds_list)})    

            # 辞書型変数に格納
            odds_umaren[race_id] = umaren_df
            
            
            #3. ワイド
            # 「ワイド」タブをクリック 
            text = browser.find_element_by_partial_link_text('ワイド')  
            browser.execute_script('arguments[0].scrollIntoView(true);', text)
            text.click()
            time.sleep(1)
            
            # オッズテーブルを作成
            table_tag_list = browser.find_elements_by_css_selector("table.Odds_Table")
            pattern_list, odds_list = [], []
            for table_tag in table_tag_list:
                html = table_tag.get_attribute('outerHTML')
                umaren_table = pd.read_html(html)[0]
                # ワイドのパターン
                pattern_list += [str(umaren_table.columns[0]) + '-' + str(x)\
                                 for x in umaren_table.iloc[:, 0]]
                # オッズ
                odds_list += list(umaren_table.iloc[:, 1])
                                  
            wide_df = pd.DataFrame(data={'pattern': pattern_list,
                                         'odds': odds_list,
                                         'race_ID': [race_id] * len(odds_list)})

            # 辞書型変数に格納
            odds_wide[race_id] = wide_df

            
            # 4.馬単
            # 「馬単」タブをクリック
            text = browser.find_element_by_partial_link_text('馬単')  
            browser.execute_script('arguments[0].scrollIntoView(true);', text)
            text.click()
            time.sleep(1)
            
            # オッズテーブルを作成
            table_tag_list = browser.find_elements_by_css_selector("table.Odds_Table")
            pattern_list, odds_list = [], []
            for table_tag in table_tag_list:
                html = table_tag.get_attribute('outerHTML')
                umaren_table = pd.read_html(html)[0]
                # 馬単のパターン
                pattern_list += [str(umaren_table.columns[0]) + '-' + str(x)\
                                 for x in umaren_table.iloc[:, 0]]
                # オッズ
                odds_list += list(umaren_table.iloc[:, 1])
                                  
            umatan_df = pd.DataFrame(data={'pattern': pattern_list,
                                           'odds': odds_list,
                                           'race_ID': [race_id] * len(odds_list)})
            
            # 辞書型変数に格納
            odds_umatan[race_id] = umatan_df
            
            
            # 5.3連複
            # 「3連複」タブをクリック
            text = browser.find_element_by_partial_link_text('3連複')  
            browser.execute_script('arguments[0].scrollIntoView(true);', text)
            text.click()
            time.sleep(1)
            
            # オッズ情報を個別に取得
            # 基準とする競走馬を選択する
            horse_list = [str(x) + ' ' + str(y)\
                          for x, y in zip(tansyo_table['馬番'], tansyo_table['馬名'])]
                  
            # 初期値
            pattern_list, odds_list = [], []
            cnt = 0
                
            for horse in horse_list:

                # プルダウンを選択
                dropdown = browser.find_element_by_id('list_select_horse')
                Select(dropdown).select_by_visible_text(horse)
                time.sleep(1)

                # オッズテーブルを作成
                table_tag_list = browser.find_elements_by_css_selector("table.Odds_Table")
                for table_tag in table_tag_list:
                    html = table_tag.get_attribute('outerHTML')
                    odds_table = pd.read_html(html)[0]
                    # 賭け方のパターン
                    pattern_list += [str(tansyo_table['馬番'].iloc[cnt, ]) + '-'\
                                     + str(odds_table.columns[0]) + '-'\
                                     + str(x)\
                                     for x in odds_table.iloc[:, 0]]
                    # オッズ
                    odds_list += list(odds_table.iloc[:, 1])
                    
                cnt += 1
            
            # オッズテーブル                      
            sanrenpuku_df = pd.DataFrame(data={'pattern': pattern_list,
                                               'odds': odds_list,
                                               'race_ID': [race_id] * len(odds_list)})

            # 辞書型変数に格納
            odds_sanrenpuku[race_id] = sanrenpuku_df
            
            
            # 3連単
            # 「3連単」タブをクリック
            text = browser.find_element_by_partial_link_text('3連単')
            browser.execute_script('arguments[0].scrollIntoView(true);', text)
            text.click()
            time.sleep(1)

            # オッズ情報を個別に取得
            # 基準とする競走馬を選択する
            horse_list = [str(x) + ' ' + str(y)\
                          for x, y in zip(tansyo_table['馬番'], tansyo_table['馬名'])]
            
            # 初期値
            pattern_list, odds_list = [], []
            cnt = 0
            
            for horse in horse_list:
                
                # プルダウンを選択
                dropdown = browser.find_element_by_id('list_select_horse')
                Select(dropdown).select_by_visible_text(horse)
                time.sleep(1)

                # オッズテーブルを作成
                table_tag_list = browser.find_elements_by_css_selector("table.Odds_Table")
                for table_tag in table_tag_list:
                    html = table_tag.get_attribute('outerHTML')
                    odds_table = pd.read_html(html)[0]
                    # 賭け方のパターン
                    pattern_list += [str(tansyo_table['馬番'].iloc[cnt, ]) + '-'\
                                     + str(odds_table.columns[0]) + '-'\
                                     + str(x)\
                                     for x in odds_table.iloc[:, 0]]
                    # オッズ
                    odds_list += list(odds_table.iloc[:, 1])
                    
                cnt += 1
                
            # オッズテーブル                      
            sanrentan_df = pd.DataFrame(data={'pattern': pattern_list,
                                              'odds': odds_list,
                                              'race_ID': [race_id] * len(odds_list)})
            
            # 辞書型変数に格納
            odds_sanrentan[race_id] = sanrentan_df


        except Exception as e:
            print(e)
            break
        
        # 1秒待機
        time.sleep(1)
        
    # DataFrame型にして一つのデータにまとめる
    odds_tansyo_df = pd.concat([odds_tansyo[key] for key in odds_tansyo])
    odds_fukusyo_df = pd.concat([odds_fukusyo[key] for key in odds_fukusyo])
    odds_umaren_df = pd.concat([odds_umaren[key] for key in odds_umaren])
    odds_umatan_df = pd.concat([odds_umatan[key] for key in odds_umatan])
    odds_wide_df = pd.concat([odds_wide[key] for key in odds_wide])
    odds_sanrenpuku_df = pd.concat([odds_sanrenpuku[key] for key in odds_sanrenpuku])
    odds_sanrentan_df = pd.concat([odds_sanrentan[key] for key in odds_sanrentan])
    
    # インデックスを振りなおす
    odds_tansyo_df.resest_index(inplace=True, drop=True)
    odds_fukusyo_df.resest_index(inplace=True, drop=True)
    odds_umaren_df.resest_index(inplace=True, drop=True)
    odds_umatan_df.resest_index(inplace=True, drop=True)
    odds_wide_df.resest_index(inplace=True, drop=True)
    odds_sanrenpuku_df.resest_index(inplace=True, drop=True)
    odds_sanrentan_df.resest_index(inplace=True, drop=True)

    return odds_tansyo_df, odds_fukusyo_df, odds_umaren_df, odds_umatan_df,\
           odds_wide_df, odds_sanrenpuku_df, odds_sanrentan_df

その他のスクレイピング

このページではnetkeibaからオッズをスクレイピングする方法を解説しましたが、それ以外のスクレイピングの方法も解説しています。

ぜひ読んでみてください。

コメント

タイトルとURLをコピーしました