#2 netkeibaのスクレイピングに挑んでみた
前回のポストは過去のレース結果を取得に関するものでした。
今回は、当週のレース情報を取得するコードです。既に取得済のレース結果を(レースIDをキーに)スクレイピングしないようにしているので、前回のコードを実行してから本スクリプトを実行した方が効率がいいです。というか、当週以外のレースまで取得してしまいます。
本スクリプト(参考に改変したコードを含む)を実行した事物が何らかの不利益を被った場合、当方は一切の責任を負いませんので予めご了承下さい。
環境_ macOS, Python(3.9.0), SQLite
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import re
import sqlite3
import json
from bikou import bikou
import math
import datetime
import traceback
import sys
def numStr(num):
if num >= 10:
return str(num)
else:
return '0' + str(num)
# 開催年
for year in range(2021, 2022):
# # レース場コード
for placeCode in range(1, 11):
# # 開催
for kaisai in range(1, 11):
# # 日目
for nitime in range(1, 13): # 念のため毎年13-15日のレンジがないか回すこと
# レース番号
for raceNum in range(1, 13):
raceId = str(year) + numStr(placeCode) + \
numStr(kaisai) + numStr(nitime) + \
numStr(raceNum)
# レース情報テーブル(過去のレース結果)のレースIDがある場合は取得しない
# sqlite3に接続
con = sqlite3.connect('horse.db')
cur = con.cursor()
raceIdTmp = raceId
sql1 = 'SELECT raceId FROM race_infos where raceId = ' + raceIdTmp
cur.execute(sql1)
query_results = cur.fetchone()
if query_results is None:
Base = 'https://race.netkeiba.com/race/shutuba.html?race_id=' # 出走表ページのベースURL
url = Base + raceId # 出走表ページのURL
# time.sleep(1) # 1秒間隔でスクレイピング
html = requests.get(url)
soup = BeautifulSoup(html.content, 'html.parser')
# レース名取得
raceName = soup.find(class_="RaceName")
if raceName is None: # レース結果テーブル取得した出来なかった場合break
break
else:
raceName = raceName.text.strip()
print(raceName)
# 開催日取得
try:
raceDateStr = soup.find(
class_="Refundlink").a.get('href')
raceDate = re.search(
r'\d+', raceDateStr).group()
raceDateYear = raceDate[0:4]
raceDateMonth = raceDate[4:6]
raceDateDay = raceDate[6:]
raceDateStr = raceDateYear + '-' + raceDateMonth + '-' + raceDateDay
except AttributeError:
pass
print(raceDateStr)
# レース情報(芝・ダート・障害・距離)取得
raceData = soup.find(
class_="RaceData01") # 芝・ダート・障害/距離取得
baba_distance = raceData.span.text.strip()
# 芝・ダート・障害 取得
baba = baba_distance[0]
# 距離取得
distance = baba_distance.replace(
baba, '').replace('m', '')
print(distance)
print(baba)
# 天気取得
weather = raceData.text[raceData.text.find(
'天候:')+3:raceData.text.find('天候:')+4]
print(weather)
# レースグレード取得
raceData = soup.find(class_="RaceData02")
raceDataList = raceData.text.splitlines()
place = raceDataList[2] # レース場
grade = raceDataList[5] # レースグレード
kinryoCondition = raceDataList[8] # 斤量条件
print(place)
print(grade)
print(kinryoCondition)
# 馬場状態取得
if soup.find(class_="Item04"):
babaCondition = soup.find(
class_="Item04").text[5:]
elif soup.find(class_="Item03"):
babaCondition = soup.find(
class_="Item03").text[5:]
else:
pass
print(babaCondition)
# 馬情報取得
horse_element = soup.select("span.HorseName")
# tagリストを文字列リストに変換
horse_list_str = []
for x in horse_element:
horse_list_str.append(str(x))
# 馬IDリストを作成
horseIdList = [] # 馬IDリスト
for horse_list in horse_list_str:
horseIdList.append(
re.sub(r"\D", "", horse_list))
del horseIdList[0]
horseIdList = [int(horseId[0:10])
for horseId in horseIdList]
print(horseIdList)
# 騎手情報取得
jockey_element = soup.select("td.Jockey")
# tagリストを文字列リストに変換
jockey_list_str = []
for x in jockey_element:
jockey_list_str.append(str(x))
jockeyIdList = [] # 騎手IDリスト
for jockey_list in jockey_list_str:
jockeyIdList.append(
re.sub(r"\D", "", jockey_list))
# jockey_elementで無駄にジョッキーIDを取得してくる可能性あるので正確なジョッキー数分にスライス
jockeyIdList = jockeyIdList[0: len(
horseIdList)]
print(jockeyIdList)
# レース結果テーブル取得
df_raceResult = pd.read_html(url, header=0)
# カラム名を物理名に変更
df_raceResult[0] = df_raceResult[0].rename(columns={'枠': 'Waku', '馬番': 'Num', '馬名': 'Horse_Name', '性齢': 'Age', '斤量': 'Kinryo',
'騎手': 'Jockey', '厩舎': 'Kyusya', '馬体重(増減)': 'Weight'})
# ジョッキー名の前に付いている記号を削除
jockeyList = []
for jockey in df_raceResult[0]['Jockey']:
if '▲' in jockey:
jockeyList.append(jockey[1:])
elif '△' in jockey:
jockeyList.append(jockey[1:])
elif '☆' in jockey:
jockeyList.append(jockey[1:])
elif '★' in jockey:
jockeyList.append(jockey[1:])
elif '◇' in jockey:
jockeyList.append(jockey[1:])
else:
jockeyList.append(jockey)
del jockeyList[0]
# 性別・年齢分割
sexList = [sex[0:1]
for sex in df_raceResult[0]['Age'].values]
ageList = [age[1:]
for age in df_raceResult[0]['Age'].values]
# 馬体重・増減分割
weightList = [str(weight)[:str(weight).find('(')]
for weight in df_raceResult[0]['Weight']]
weightChageList = [str(weight)[str(weight).find('(') + 1: str(weight).find(')')]
for weight in df_raceResult[0]['Weight']]
print(weightList)
print(weightChageList)
df_raceResult[0] = df_raceResult[0].drop(
'Age', axis=1)
df_raceResult[0] = df_raceResult[0].drop(
'印', axis=1)
df_raceResult[0] = df_raceResult[0].drop(
'人気', axis=1)
df_raceResult[0] = df_raceResult[0].drop(
'Unnamed: 9', axis=1)
df_raceResult[0] = df_raceResult[0].drop(
'お気に入り馬.1', axis=1)
df_raceResult[0] = df_raceResult[0].drop(
'お気に入り馬', axis=1)
df_raceResult[0] = df_raceResult[0].drop([0])
del weightList[0]
del weightChageList[0]
del sexList[0]
del ageList[0]
df_raceResult[0]['Jockey'] = jockeyList
df_raceResult[0]['Weight'] = weightList
df_raceResult[0]['WeightChange'] = weightChageList
df_raceResult[0]['sex'] = sexList # 性別
df_raceResult[0]['age'] = ageList # 年齢
df_raceResult[0]['raceId'] = [
int(raceId)] * len(df_raceResult[0]) # レース番号を付加
# 馬IDを付加
df_raceResult[0]['horseID'] = horseIdList
# 騎手IDを付加
df_raceResult[0]['jockeyID'] = jockeyIdList
df_raceResult[0]['raceName'] = [raceName] * \
len(df_raceResult[0]) # レース名を付加
df_raceResult[0]['baba'] = [baba] * \
len(df_raceResult[0]) # 馬場を付加
df_raceResult[0]['distance'] = [distance] * \
len(df_raceResult[0]) # 距離を付加
df_raceResult[0]['babacondition'] = [
babaCondition] * len(df_raceResult[0]) # 馬場状態を付加
df_raceResult[0]['place'] = [place] * \
len(df_raceResult[0]) # レース場所を付加
df_raceResult[0]['grade'] = [grade] * \
len(df_raceResult[0]) # グレードを付加
df_raceResult[0]['kinryoCondition'] = [
kinryoCondition] * len(df_raceResult[0]) # 斤量条件を付加
df_raceResult[0]['raceDate'] = [raceDateStr] * \
len(df_raceResult[0]) # 開催日を付加
df_raceResult.append
print(df_raceResult[0])
# レース情報フレーム作成
df_raceInfo = pd.DataFrame([[raceId, raceName, raceDateStr, weather, place, baba, grade, distance, babaCondition, kinryoCondition]], columns=[
'raceId', 'raceName', 'raceDate', 'weather', 'place', 'baba', 'grade', 'distance', 'babaCondition', 'kinryoCondition'])
# sqlite3に接続
con = sqlite3.connect('horse.db')
cur = con.cursor()
# レース最新情報挿入
try: # raceIDの衝突が起きたとき例外発生
# レース結果情報挿入
df_raceResult[0].to_sql('shutuba', con,
if_exists='append', index=None)
except sqlite3.IntegrityError:
pass
try:
# レース情報挿入
df_raceInfo.to_sql('shutuba_infos', con,
if_exists='append', index=None)
except sqlite3.IntegrityError:
pass
con.commit()
con.close()
中央競馬ランキング
↑↑↑