スクレイピングは節度を守って行いましょう。(ここに書いてあるものは全て1秒以上の間隔をあけています)
また、出てくるデータが若干汚いですが、後で整形すればいいやという気持ちでやっています。
また、掲載にあたって少しコードをいじったので、動かないことがあります。
2022/02/19追記
コードまとめました。競艇と競輪のコードもあります。
個人用なのでドキュメントとか無いですけど、それでもよければ使ってみてください。
https://github.com/apiss2/scraping
レースIDの取得
netkeibaでは、各レースがレースIDという数字で管理されているようです。レース一覧のページから、当日の各レースについてレースIDを取得できます。
def get_raceid_list_from_date(today):
date = f'{today.year:04}{today.month:02}{today.day:02}'
url = 'https://db.netkeiba.com/race/list/' + date
html = requests.get(url)
html.encoding = "EUC-JP"
soup = BeautifulSoup(html.text, "html.parser")
race_list = soup.find('div', attrs={"class": 'race_list fc'})
if race_list is None:
return list()
a_tag_list = race_list.find_all('a')
href_list = [a_tag.get('href') for a_tag in a_tag_list]
race_id_list = list()
for href in href_list:
for race_id in re.findall('[0-9]{12}', href):
race_id_list.append(race_id)
return list(set(race_id_list))
使用例
1年分のレースIDの一覧を取得。
def scraping_race_id_list(year):
today = datetime.date(year, 1, 1)
race_id_list = list()
for i in tqdm(range(365)):
race_id_list += get_raceid_list_from_date(today)
today = today + relativedelta(days=1)
time.sleep(1)
return race_id_list
レース情報の取得
上記までに取得したレースIDを使って各レースの情報を取得します。
また、以降の関数の入力は以下のコードで得たものを入力とします。
url = f"https://db.netkeiba.com/race/{race_id}"
html = requests.get(url)
html.encoding = "EUC-JP"
soup = BeautifulSoup(html.text, "html.parser")
レース概要の把握
レース名、天候、レースの種類、コースの長さ、馬場の状態、日付など、レースに関する様々な情報を取得します。
ここらへんは改良の余地があると思います。 (見づらくて申し訳ないです)
def get_race_info(soup):
race_name = soup.find("dl", attrs={"class": "racedata fc"}).find('h1').text
race_info_list = [race_name]
race_info_list += soup.find("div", attrs={"class": "data_intro"}).find_all("p")[0].find('span').text.replace('\xa0', '').split('/')
race_info_list += soup.find("div", attrs={"class": "data_intro"}).find_all("p")[1].text.replace('\xa0', '').split(' ')
return race_info_list
def parse_race_info(race_info_list):
assert 8 <= len(race_info_list) <= 9
race_info = [race_info_list[0]]
race_info.append('右' if '右' in race_info_list[1] else '左' if '右' in race_info_list[1] else '不明')
race_info.append('障害' if '障' in race_info_list[1] else 'ダート' if 'ダ' in race_info_list[1] else '芝')
race_info.append(race_info_list[3].split(' : ')[-1]) # 馬場状態
race_info.append(re.findall('\d\d\d0m', race_info_list[1])[0][:-1]) # 距離
race_info.append(race_info_list[2].split(' : ')[1]) # 天候
race_info.append(race_info_list[5]) # 日次
race_info.append(race_info_list[4].split(' : ')[1]) # 発走
# 開催場所、タイミング
hold_n = re.search('\d+回', race_info_list[6])
day_n = re.search('\d+日目', race_info_list[6])
racecourse = race_info_list[6][hold_n.span()[1]:day_n.span()[0]]
race_info.append(hold_n[0])
race_info.append(day_n[0])
race_info.append(racecourse)
# 条件
s = race_info_list[7] if len(race_info_list)==8 else race_info_list[7] + race_info_list[8]
sep = s.find('[') if (s.find('[') != -1) and (s.find('[') < s.find('(')) else s.find('(')
race_info.append(s[:sep])
race_info.append(s[sep:]) # ここが詳しく分離できない
return race_info
また、レース種別が芝とダートの組み合わせになっており、馬場状態もそれに伴って複数出現するものもありますが、それは面倒なので考慮していません。
出走する各馬の情報を取得
該当するレースに出走する馬と騎手についての情報を取得します。各馬について詳細情報(過去のレース結果一覧など)を取得する方法については気が向いたら書きます。
def get_horse_info(html, race_id):
# 出走馬のテーブルデータを取得
df = pd.read_html(html.text)[0]
df.index = [race_id] * len(main_df)
# 馬ID、騎手IDを取得
horse_idlist = get_idlist_from_table(soup, 'horse')
df["horse_id"] = horse_idlist
jockey_idlist = get_idlist_from_table(soup, 'jockey')
df["jockey_id"] = jockey_idlist
return df
def get_idlist_from_table(soup, target):
# 参照先のURLから馬や騎手の固有IDを取得
idlist = list()
atag_list = soup.find("table", attrs={"summary": "レース結果"}).find_all(
"a", attrs={"href": re.compile(f"^/{target}")})
for atag in atag_list:
target_id = re.findall(r"\d+", atag["href"])
idlist.append(target_id[0])
return idlist
払戻金の取得
def get_return_table(html, race_id):
# 改行が含まれているので、置換してから読み込み
tables = pd.read_html(html.text.replace('<br />', 'sep'))
return_df = pd.concat(tables[1:3])
return_df.index = [race_id] * len(return_df)
return return_df
レース情報についてのまとめ
def scraping_race_table(race_id):
url = f"https://db.netkeiba.com/race/{race_id}"
html = requests.get(url)
html.encoding = "EUC-JP"
soup = BeautifulSoup(html.text, "html.parser")
# 出走馬情報のスクレイピング
main_df = get_horse_info(html, race_id)
# レース情報のスクレイピング
race_info = get_race_info(soup)
race_info = parse_race_info(race_info)
# 払い戻し情報のスクレイピング
return_df = get_return_table(html, race_id)
return main_df, return_df, race_info
使用例
target_dir = 'path/to/save_dir'
race_id_list = scraping_race_id_list(2020)
for race_id in race_id_list:
main_df, return_df, race_info = scraping_race_table(race_id)
# 保存
path = os.path.join(target_dir, 'race_info.csv')
with open(, 'a', encoding='utf-8') as f:
s = ','.join(race_info)
f.write(f'{race_id},' + s+'\n')
path = os.path.join(target_dir, 'horse', f'{race_id}.csv')
main_df.to_csv(path, encoding='utf-8')
path = os.path.join(target_dir, 'prize', f'{race_id}.csv')
return_df.to_csv(path, encoding='utf-8')
time.sleep(1)