本記事の目的
レース結果 & レース情報

作業概要
本サイトでは、下記の手順で「netkeiba.com」からレース結果を取得します。
- ②レースデータを取得 ←このページ

このページでは、「②レースデータを取得」を解説するよ
本ページでは、「①開催レースのURL一覧を取得」で取得した開催レースURLを使用します。
まだ、ご覧になっていない方は是非読んでみてください!
②レースデータを取得
ソースコード
""" メソッド:レースデータ取得 """
# ライブラリ
from tqdm import tqdm
import urllib.request
from bs4 import BeautifulSoup
import time
import pandas as pd
def scrape_data(url_df):
# 初期値
race_result = pd.DataFrame()
date_list = url_df['date']
url_list = url_df['url']
cnt = 0
try:
# tqdmで処理状況を見える化 --------------------- (※1)
for url in tqdm(url_list):
# 1秒間停止 --------------------- (※2)
time.sleep(1)
# レース結果を取得 --------------------- (※3)
df = pd.read_html(url)[0]
# WEBデータを解析
html = urllib.request.urlopen(url).read()
soup = BeautifulSoup(html, 'html.parser')
# 競走馬IDを追加する --------------------- (※4)
div_result = soup.find('div', class_='ResultTableWrap')
span_horse = div_result.find_all('span', class_='Horse_Name')
horse_id_list = []
for span in span_horse:
href = span.a.attrs['href']
horse_id = href.split('/')[-1]
horse_id_list.append(horse_id)
df['horse_ID'] = horse_id_list
# 騎手IDを追加する --------------------- (※4)
td_jockey = div_result.find_all('td', class_='Jockey')
jockey_id_list = []
for td in td_jockey:
href = td.a.attrs['href']
jockey_id = href.split('/')[-2]
jockey_id_list.append(jockey_id)
df['jockey_ID'] = jockey_id_list
# 「開催年月日」「競馬場ID」「開催回数」「開催日」「レース回数」を追加
race_id = url.split('race_id=')[1][0:12]
df['date'] = [date_list[cnt]] * len(df)
df['area_ID'] = [race_id[4:6]] * len(df)
df['race_month'] = [race_id[6:8]] * len(df)
df['race_day'] = [race_id[8:10]] * len(df)
df['race_num'] = [race_id[10:12]] * len(df)
# レース情報を取得
div_01 = soup.find('div', class_='RaceData01').text
div_02 = soup.find('div', class_='RaceData02').text
# 「レース種類」を抽出 (0:ダート、1:芝、2:障害、3:その他)
text = div_01.split('/')[1]
if 'ダ' in text:
race_type = 0
elif '芝' in text:
race_type = 1
elif '障' in text:
race_type = 2
else:
race_type = 3
# 「レース長」を抽出
race_len = int(text.split('m')[0][2:])
# 「天候」を抽出 (0:曇、1:晴、2:雨、3:小雨、4:小雪、5:雪、6:その他)
text = div_01.split('/')[2]
if '曇' in text:
weather = 0
elif '晴' in text:
weather = 1
elif '雨' in text:
weather = 2
elif '小雨' in text:
weather = 3
elif '小雪' in text:
weather = 4
elif '雪' in text:
weather = 5
else:
weather = 6
# 「馬場状態」を抽出 (0:良、1:稍重、2:稍、3:重、4:不良、5:その他)
text = div_01.split('/')[3]
if '良' in text:
ground_state = 0
elif '稍重' in text:
ground_state = 1
elif '稍' in text:
ground_state = 2
elif '重' in text:
ground_state = 3
elif '不良' in text:
ground_state = 4
else:
ground_state = 5
# 「レースクラス」を抽出
race_class = div_02.split('\n')[5]
# レース情報を追加
df['race_type'] = [race_type] * len(df)
df['race_len'] = [race_len] * len(df)
df['weather'] = [weather] * len(df)
df['ground_state'] = [ground_state] * len(df)
df['race_class'] = [race_class] * len(df)
race_result = race_result.append(df)
# インデックスを振りなおす
race_result = race_result.reset_index(drop=True)
return race_result
except Exception as e:
print('■取得失敗URL : ', url)
print('■エラー内容 : ', e)
# インデックスを振りなおす
race_result = race_result.reset_index(drop=True)
return race_result
◆メソッドの引数と戻り値◆
カテゴリ | 変数 | 型 | 説明 |
引数 | url_df | DataFrame | 開催レースのURL |
戻り値 | race_data | DataFrame | レースデータ |

引数の “url_df” は、「開催レースのURL一覧を取得」で解説しているメソッドの戻り値だよ!
解説
プログレスバーを表示する(※1)
本処理では、
tqdm()
を使用することでプログレスバーを表示しています。
処理状況が分かるようになるので、処理時間が長いループで利用することをおすすめします。
# tqdmで処理状況を見える化 --------------------- (※1)
for url in tqdm(url_list):
時間間隔を設定する(※2)
スクレイピングする際には、Webサイトへの負荷を考慮する必要があります。
Webサーバーに負荷をかけると、他のユーザーがそのWebサイトを参照できなかったり、ひどい場合はサーバーが落ちてしまう場合もあります。
Pythonオンライン学習サービス PyQ
そのような迷惑をかけないためにも、クローラーは間隔をあけてWebサーバーにアクセスするといった対応が必要となります。最低1秒以上は間隔をあけるようにしましょう。
上記のように、スクレイピングする際には最低1秒以上は間隔を空けましょう。
time.sleep()
# 1秒間停止 --------------------- (※2)
time.sleep(1)
レース結果を取得する(※3)
スクレイピングする際には、まずは欲しい情報のHTMLを確認します。

そうすると、今回入手するデータは<table>タグであることが分かります。
この場合は、pandasの
read_html()
を使用することで簡単にレース結果を入手できます。
# レース結果を取得 --------------------- (※3)
df = pd.read_html(url)[0]
「馬名」と「騎手」をIDを追加する(※4)
入手したデータを機械学習で使用する際には、数値以外が使えないケースがあります。
なので本処理では、「馬名」「騎手」のID情報をWebデータから入手します。
ここでHTMLを確認してみると、

ということが分かります。
本処理では、”Beautiful Soup”の
find()
を使用して、競走馬ID・騎手IDを入手します。
- ①Beautiful Soupのインスタンスを作成
- ②「class属性」が「ResultTableWrap」である、<div>タグの要素を抽出
- ③取得した要素の中から、「class属性」が「Horse_Name」、「Jockey」である<span>タグ・<td>タグの要素を抽出
- ④取得した要素の中から、<aタグ>の「href属性」を取得
- ⑤文字列を編集して、競走馬IDと騎手IDを取得
# WEBデータを解析
html = urllib.request.urlopen(url).read()
soup = BeautifulSoup(html, 'html.parser')
# 競走馬IDを追加する --------------------- (※4)
div_result = soup.find('div', class_='ResultTableWrap')
span_horse = div_result.find_all('span', class_='Horse_Name')
horse_id_list = []
for span in span_horse:
href = span.a.attrs['href']
horse_id = href.split('/')[-1]
horse_id_list.append(horse_id)
df['horse_ID'] = horse_id_list
# 騎手IDを追加する --------------------- (※4)
td_jockey = div_result.find_all('td', class_='Jockey')
jockey_id_list = []
for td in td_jockey:
href = td.a.attrs['href']
jockey_id = href.split('/')[-2]
jockey_id_list.append(jockey_id)
df['jockey_ID'] = jockey_id_list
レース情報を取得(※5)
「レース結果を取得する(※3)」と同様にHTMLを確認すると、

ということが分かります。
本処理でも、”Beautiful Soup”の
find()
を使用して、レース情報(レース種類、レース長、天候、馬場状態、レースクラス)を取得します。
# レース情報を取得 --------------------- (※5)
div_01 = soup.find('div', class_='RaceData01').text
div_02 = soup.find('div', class_='RaceData02').text
# 「レース種類」を抽出 (0:ダート、1:芝、2:障害、3:その他)
text = div_01.split('/')[1]
if 'ダ' in text:
race_type = 0
elif '芝' in text:
race_type = 1
elif '障' in text:
race_type = 2
else:
race_type = 3
# 「レース長」を抽出
race_len = int(text.split('m')[0][2:])
# 「天候」を抽出 (0:曇、1:晴、2:雨、3:小雨、4:小雪、5:雪、6:その他)
text = div_01.split('/')[2]
if '曇' in text:
weather = 0
elif '晴' in text:
weather = 1
elif '雨' in text:
weather = 2
elif '小雨' in text:
weather = 3
elif '小雪' in text:
weather = 4
elif '雪' in text:
weather = 5
else:
weather = 6
# 「馬場状態」を抽出 (0:良、1:稍重、2:稍、3:重、4:不良、5:その他)
text = div_01.split('/')[3]
if '良' in text:
ground_state = 0
elif '稍重' in text:
ground_state = 1
elif '稍' in text:
ground_state = 2
elif '重' in text:
ground_state = 3
elif '不良' in text:
ground_state = 4
else:
ground_state = 5
# 「レースクラス」を抽出
race_class = div_02.split('\n')[5]
「df」にレース情報を追加(※6)
入手したレース情報は文字列型のため、このままデータフレーム型変数「df」に追加できません。
そこで本処理では、↓のように記述することで
[文字列] * n ※n:「df」のレコード件数
入手したデータを「df」のレコード件数分だけ繰り返してリスト化し、「df」に追加しています。
# レース情報を追加 --------------------- (※6)
df['race_type'] = [race_type] * len(df)
df['race_len'] = [race_len] * len(df)
df['weather'] = [weather] * len(df)
df['ground_state'] = [ground_state] * len(df)
df['race_class'] = [race_class] * len(df)
例外発生時は「取得失敗URL」と「エラー内容」を出力する(※7)
本処理では意図しないデータが原因で、例外が発生する可能性があります。
そこで、
try:
正常処理
except Exception as e:
異常処理
という構文を使用することで、例外が発生した際の「取得失敗URL」と「例外内容」を出力しています。
# 例外発生時は「取得失敗URL」と「例外内容」を出力する --------------------- (※7)
except Exception as e:
print('■取得失敗URL : ', url)
print('■例外内容 : ', e)
参考図書
本記事の内容は、以下の図書を参考にしています。
本書は、下記のようにスクレイピングについてとても詳しく解説されています。

本書の内容は「BeautifulSoupでスクレイピング」を参考にしているよ!
本書は、上記以外にも
等も内容に含まれています。
Python初心者にも理解しやすい内容になっているので、ぜひ参考にしてください。
次回の内容
次回は、本記事で取得したレースデータを、機械学習で使えるように加工します。
ぜひ読んでみてください!
コメント