この記事では、タダで競走馬データベースを作成する方法を説明します。
無料作成には、PythonとSQLiteの活用が前提となります。
必要なライブラリ
スクリプトを実行するには、以下のライブラリが必要です。
- requests
- BeautifulSoup
- pandas
- sqlite3
これらのライブラリは、pip installコマンドを使ってインストールできます。
その際、以下の記事が参考となります。
主要な機能
以下が、競走馬データベースを作成するスクリプトになります。
import requests
from bs4 import BeautifulSoup
import re
import math
import pandas as pd
import sqlite3
import time
def insert_or_update_data(df, conn):
"""データフレームの各行をチェックし、データベースに挿入または更新します"""
cursor = conn.cursor()
for index, row in df.iterrows():
cursor.execute('SELECT horse_id FROM horses WHERE horse_id = ?', (row['horse_id'],))
data = cursor.fetchone()
if data:
# レコードが存在する場合、必要に応じて更新処理を行う
cursor.execute('''
UPDATE horses SET
horse_name=?, gender=?, birth_year=?, stable=?, father=?, mother=?,
maternal_grandfather=?, owner=?, breeder=?, total_prize_money_10thou_yen=?
WHERE horse_id=?
''', (row['horse_name'], row['gender'], row['birth_year'], row['stable'], row['father'], row['mother'],
row['maternal_grandfather'], row['owner'], row['breeder'], row['total_prize_money_10thou_yen'],
row['horse_id']))
else:
# レコードが存在しない場合、新たに挿入
cursor.execute('''
INSERT INTO horses (horse_id, horse_name, gender, birth_year, stable, father, mother,
maternal_grandfather, owner, breeder, total_prize_money_10thou_yen)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (row['horse_id'], row['horse_name'], row['gender'], row['birth_year'], row['stable'], row['father'],
row['mother'],
row['maternal_grandfather'], row['owner'], row['breeder'], row['total_prize_money_10thou_yen']))
conn.commit()
def get_total_pages(url):
# requestsを使って、ウェブページを取得する
response = requests.get(url)
# WebページのHTMLをパースするためにBeautifulSoupを使う
soup = BeautifulSoup(response.text, 'html.parser')
# 'pager'クラスを持つdivを見つける
pager_div = soup.find('div', class_='pager')
# divのテキストから数字を抽出するために正規表現を使用する
numbers = re.findall(r'\d+', pager_div.text.replace(',', '')) # カンマを削除して数字のみを抽出
# 数値が見つからない場合、エラーメッセージを表示して処理を終了する
if not numbers:
return None # ここで処理を終了
# 全件数を取得し、ページ総数を計算する
total_items = int(numbers[0]) # 「8,804」に相当する数値を整数型に変換
items_per_page = 20
total_pages = math.ceil(total_items / items_per_page) # 1ページあたりのアイテム数で割り、切り上げてページ総数を計算
return total_pages
def main():
# 列名を英語に変換するためのマッピング辞書を定義
column_mapping = {
'馬ID': 'horse_id',
'馬名': 'horse_name',
'性': 'gender',
'生年': 'birth_year',
'厩舎': 'stable',
'父': 'father',
'母': 'mother',
'母父': 'maternal_grandfather',
'馬主': 'owner',
'生産者': 'breeder',
'総賞金(万円)': 'total_prize_money_10thou_yen'
}
# ページ数を取得
base_url = 'https://db.netkeiba.com/?pid=horse_list&word=&match=partial_match&sire=&keito=&mare=&bms=&trainer=&owner=&breeder=&under_age=2&over_age=none&prize_min=&prize_max=&sort=prize&list=20&act=1&shozoku%5B1%5D=1&shozoku%5B2%5D=2'
total_pages = get_total_pages(base_url)
# 戻り値をチェックする
if total_pages is None:
# エラー処理
print("ページ数を取得できませんでした。URLを確認してください。")
return
# DB初期化・接続
db_name = 'keiba.db'
conn = connect_database(db_name)
cursor = conn.cursor()
create_table(cursor)
# 各ページにアクセスするためのURLを生成し、アクセスする
for page_number in range(1, total_pages + 1):
page_url = f"{base_url}&page={page_number}"
print(page_url)
# データフレーム取得
df = fetch_page_data(page_url)
# データフレームの列名を英語に変換
df = df.rename(columns=column_mapping)
# データ登録
insert_or_update_data(df, conn)
time.sleep(1)
conn.close()
def fetch_page_data(page_url):
response = requests.get(page_url)
response.encoding = 'euc-jp' # エンコーディングをEUC-JPに設定
soup = BeautifulSoup(response.text, 'html.parser')
table = soup.find('table', class_='nk_tb_common race_table_01')
# テーブルヘッダーを抽出
headers = ['馬ID'] + [th.get_text(strip=True) for th in table.find('tr').find_all('th')]
# データ行と馬IDを抽出
rows = []
for tr in table.find_all('tr')[1:]: # ヘッダー行をスキップ
# 馬名のリンクからIDを抽出
horse_link = tr.find('a', href=True)
horse_id = horse_link['href'].split('/')[-2] if horse_link else None
cols = [td.get_text(strip=True) for td in tr.find_all('td')]
rows.append([horse_id] + cols) # IDを先頭に追加
# pandas DataFrameに変換
df = pd.DataFrame(rows, columns=headers)
# 列名が空の列を探し、そのインデックスを取得
empty_columns = [col for col in df.columns if col == '']
# 空の列名を持つ列を削除
df.drop(columns=empty_columns, inplace=True)
return df
def connect_database(filename):
"""データベースに接続(またはデータベースファイルを作成)"""
return sqlite3.connect(filename)
def create_table(cursor):
"""馬のデータを格納するテーブルを作成"""
cursor.execute('DROP TABLE IF EXISTS horses')
cursor.execute('''
CREATE TABLE horses (
horse_id TEXT PRIMARY KEY,
horse_name TEXT,
gender TEXT,
birth_year TEXT,
stable TEXT,
father TEXT,
mother TEXT,
maternal_grandfather TEXT,
owner TEXT,
breeder TEXT,
total_prize_money_10thou_yen REAL
)
''')
if __name__ == '__main__':
main()
スクリプトには以下の主要な関数があります。
- get_total_pages(url): 指定されたURLから全ページ数を取得。
- fetch_page_data(page_url): 指定されたURLから競走馬のデータを取得し、pandasデータフレームに変換。
- insert_or_update_data(df, conn): pandasデータフレームの各行をチェックし、SQLiteデータベースに挿入または更新。
- connect_database(filename): SQLiteデータベースに接続。
- create_table(cursor): 競走馬のデータを格納するテーブルを作成。
データの取得とデータベースへの保存
スクリプトは、最初にbase_urlを使って全ページ数を取得します。
このbase_urlは、netkeibaにおける「競走馬検索」で作成したモノです。
検索時の条件は、以下となります。
- 現役馬
- 中央所属(関東・関西)
次に、SQLiteデータベースに接続し、horsesテーブルを作成(または既存のテーブルを削除)となります。
各ページのURLを生成し、fetch_page_data(page_url)を使ってデータを取得します。
取得したデータの列名は、column_mapping辞書を使って英語に変換されます。
日本語のままだと、プログラム上で扱いにくいです。
データは、insert_or_update_data(df, conn)を使ってデータベースに挿入または更新されます。
次のページに移動する前に、time.sleep(1)を使って1秒のウェイトを入れます。
これは、連続的なリクエストによってサーバーに過剰な負荷がかかることを防ぐために重要です。
データベーススキーマ
horsesテーブルには、馬ID、馬名、性別、生年、厩舎、父馬、母馬、母父、馬主、生産者、総賞金(万円)のカラムがあります。
基本的には、netkeiba上の表示結果そのままです。
ただし、「馬ID」だけは別途加えています。
https://db.netkeiba.com/horse/2012102013/
「2012102013」が、馬IDとなります。
データベースには、以下のように登録されることになります。

まとめ
このスクリプトを使うことで、netkeiba.comから競走馬のデータを効率的に取得し、SQLiteデータベースに保存することができます。保存されたデータは、競馬の分析や予測モデルの構築などに活用できます。
スクレイピングを行う際は、サーバーへの負荷を考慮したスクレイピングを心がけることが重要です。
なお、スクレイピングに関しては国も普通にやっています。





