この記事では、競馬好きの方に人気のサイト「netkeiba.com」から、Pythonを使って競走馬の戦績を自動で取得する方法を、やさしく解説します。
事前準備
まずはPythonで必要なライブラリをインストールしましょう。ターミナルやコマンドプロンプトで以下を実行します:
pip install selenium beautifulsoup4 pandas openpyxl requests
コード全体(完全版)
以下が、netkeibaから競走馬の戦績を取得するPythonコードの全体です。
import requests
from bs4 import BeautifulSoup
import pandas as pd
import os
from openpyxl import load_workbook
import re
import time
# 定数の定義
OUTPUT_PATH = r"C:\Users\User\Desktop"
# RACE_URL は使わず、後で動的に生成します
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Accept-Language": "ja,en;q=0.9"
}
# 小数点1桁に丸める対象の数値項目
SPECIAL_COLUMNS = {"斤量", "オッズ", "着差", "上り", "賞金"}
# 強制的にfloat型に変換する対象(「タイム」は必ずfloatで保存する)
FORCE_FLOAT_COLUMNS = {"タイム"}
def convert_value(x):
"""
各セルの値が数字のみの場合、(カンマ除去後)floatまたはintに変換する。
例:"55" → int(55)、"5.6" → float(5.6)
"""
if isinstance(x, str):
x_clean = x.replace(',', '').strip()
if re.fullmatch(r'\d+(\.\d+)?', x_clean):
return float(x_clean) if '.' in x_clean else int(x_clean)
else:
return x
return x
def convert_df_numeric_full(df):
"""
DataFrame 内の各セルについて、値が数字のみの場合は数値型に変換し、
SPECIAL_COLUMNS の項目については float にして小数点1桁に丸め、
FORCE_FLOAT_COLUMNS の項目は必ず float 型に変換する。
"""
df = df.applymap(convert_value)
for col in SPECIAL_COLUMNS:
if col in df.columns:
df[col] = df[col].apply(lambda x: round(float(x), 1) if isinstance(x, (int, float)) else x)
for col in FORCE_FLOAT_COLUMNS:
if col in df.columns:
df[col] = df[col].apply(lambda x: float(x) if isinstance(x, (int, float)) else x)
return df
def fetch_race_data(url):
"""
Selenium を用いて、JavaScript 実行後のレースページの HTML を取得する
"""
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--disable-gpu")
driver = webdriver.Chrome(options=chrome_options)
driver.get(url)
time.sleep(5) # JavaScript実行待ち(必要に応じて調整)
html = driver.page_source
driver.quit()
return BeautifulSoup(html, "html.parser")
def get_race_title(soup):
"""レースのタイトル情報を取得する関数(既存の処理)"""
race_name = soup.find("h1", class_="RaceName").text.strip()
race_data = soup.find("div", class_="RaceData02")
spans = race_data.find_all("span")
title = [
race_name,
spans[0].text.strip(),
spans[1].text.strip(),
spans[2].text.strip()
]
return " ".join(title)
def get_race_number(soup):
"""
レース番号の要素 <span class="RaceNum"> のテキストを取得します。
"""
race_num_element = soup.find("span", class_="RaceNum")
if race_num_element:
return race_num_element.get_text(strip=True)
return ""
def get_additional_race_info(soup):
"""
レースページから「レース名」「場名」「芝・ダート」「距離」「天候」「馬場」を取得する関数
※ h1, RaceData01, RaceData02 の各要素から取得
"""
h1 = soup.find("h1", class_="RaceName")
race_name = h1.find(string=True, recursive=False).strip() if h1 and h1.find(string=True, recursive=False) else ""
race_data02 = soup.find("div", class_="RaceData02")
field_name = race_data02.find_all("span")[1].text.strip() if race_data02 and len(race_data02.find_all("span")) > 1 else ""
race_data01 = soup.find("div", class_="RaceData01")
if race_data01:
span_list = race_data01.find_all("span")
if span_list:
surface_distance_text = span_list[0].text.strip()
m = re.match(r'([芝ダート]+)(\d+m?)', surface_distance_text)
if m:
surface = m.group(1)
distance = re.sub(r'\D', '', m.group(2))
else:
surface = ""
distance = ""
else:
surface = ""
distance = ""
race_data01_text = race_data01.get_text(" ", strip=True)
m_weather = re.search(r'天候:([^\s/]+)', race_data01_text)
weather = m_weather.group(1).strip() if m_weather else ""
m_track = re.search(r'馬場:([^\s/]+)', race_data01_text)
track = m_track.group(1).strip() if m_track else ""
else:
surface = ""
distance = ""
weather = ""
track = ""
return {
"レース名": race_name,
"場名": field_name,
"芝・ダート": surface,
"距離": distance,
"天候": weather,
"馬場": track
}
def parse_race_table(soup):
"""
レーステーブルを解析して DataFrame を作成する関数
不要な項目(例:"印", "馬メモ切替", "お気に入り馬", "マスターレース別馬メモ切替")は除外し、
"オッズ更新_リンク" は取得しないように調整。
"""
table = soup.find("table")
if not table:
print("テーブルが見つかりませんでした。")
return None
# ヘッダーは <thead> 内の最初の行を利用
thead = table.find("thead")
if not thead:
print("theadが見つかりませんでした。")
return None
first_header_row = thead.find_all("tr")[0]
all_headers = [th.text.strip() for th in first_header_row.find_all("th")]
# 除外するヘッダー
ignored_headers = {"印", "馬メモ切替", "お気に入り馬", "マスターレース別馬メモ切替", "更新"}
# 除外するリンクヘッダー
ignored_link_headers = {
"枠_リンク", "馬番_リンク", "印_リンク", "性齢_リンク", "斤量_リンク",
"馬体重(増減)_リンク", "更新_リンク", "人気_リンク",
"オッズ 更新_リンク"
}
keep_indices = []
kept_headers = []
for i, header in enumerate(all_headers):
if header in ignored_headers:
continue
keep_indices.append(i)
kept_headers.append(header)
# リンクヘッダーを決定
kept_link_headers = []
for header in kept_headers:
link_header = f"{header}_リンク"
if link_header in ignored_link_headers:
kept_link_headers.append(None)
else:
kept_link_headers.append(link_header)
final_columns = kept_headers + [lh for lh in kept_link_headers if lh]
tbody = table.find("tbody")
if not tbody:
print("tbodyが見つかりませんでした。")
return None
data_rows = tbody.find_all("tr")
data = []
for row in data_rows:
cells = row.find_all(["td", "th"])
row_text = [cells[i].text.strip() for i in keep_indices if i < len(cells)]
row_links = []
for idx, i in enumerate(keep_indices):
if i < len(cells):
a_tag = cells[i].find("a")
link = a_tag["href"] if a_tag else ""
if kept_link_headers[idx] is not None:
row_links.append(link)
data.append(row_text + row_links)
df = pd.DataFrame(data, columns=final_columns)
# 念のため「オッズ 更新_リンク」および「オッズ更新_リンク」を削除
df.drop(
columns=[col for col in df.columns if col in ["オッズ 更新_リンク", "オッズ更新_リンク"]],
inplace=True,
errors="ignore"
)
return df
def get_horse_data(link):
"""
個別の馬の戦績データと血統情報を取得する関数
・馬ページからレース結果テーブル(class="db_h_race_results nk_tb_common")を取得
・同一ページ内の血統情報(<div class="db_prof_box">内の<table class="blood_table">)も取得
※血統は、テーブル内の各リンクテキストをリストとして返します。
"""
response = requests.get(link, headers=HEADERS)
if response.status_code != 200:
print(f"ステータスコードが200ではありません。HTTPコード: {response.status_code}")
return None
soup = BeautifulSoup(response.content, "html.parser")
# --- 血統情報の取得 ---
bloodline = []
blood_div = soup.find("div", class_="db_prof_box")
if blood_div:
blood_table = blood_div.find("table", class_="blood_table")
if blood_table:
a_tags = blood_table.find_all("a")
bloodline = [a.text.strip() for a in a_tags]
# --- ここまで ---
table = soup.find("table", class_="db_h_race_results nk_tb_common")
if not table:
print("指定されたテーブルが見つかりませんでした。")
return None
header = [th.text.strip() for th in table.find("thead").find_all("th")]
header = [h if "オッズ" not in h else "オッズ" for h in header]
rows = table.find("tbody").find_all("tr")
data = [[col.text.strip() for col in row.find_all("td")] for row in rows]
df = pd.DataFrame(data, columns=header)
return df, bloodline
def get_jockey_name(url):
"""
指定されたURLから騎手名を取得する関数
<div class="db_head_name fc">内の<h1>要素の、 より前の部分のテキストを返します。
"""
response = requests.get(url, headers=HEADERS)
if response.status_code != 200:
print(f"騎手ページの取得に失敗しました。HTTPコード: {response.status_code}")
return None
soup = BeautifulSoup(response.content, "html.parser")
jockey_div = soup.find("div", class_="db_head_name fc")
if jockey_div:
h1 = jockey_div.find("h1")
if h1:
text = h1.get_text(strip=True)
if "\xa0" in text:
return text.split("\xa0")[0]
else:
return text
return None
def get_trainer_name(url):
"""
指定されたURLから厩舎名を取得する関数
<div class="db_head_name fc"> 内の <h1> 要素のテキストを取得し、
(実際はUnicodeの \xa0 )以降の文字列を除外して返す。
"""
response = requests.get(url, headers=HEADERS)
if response.status_code != 200:
print(f"厩舎ページの取得に失敗しました。HTTPコード: {response.status_code}")
return None
soup = BeautifulSoup(response.content, "html.parser")
trainer_div = soup.find("div", class_="db_head_name fc")
if trainer_div:
h1 = trainer_div.find("h1")
if h1:
text = h1.get_text(strip=True)
trainer_name = text.split('\xa0')[0].strip()
return trainer_name
return None
def save_excel_with_title(df, path, title):
"""
DataFrame を Excel に保存し、ブックのタイトルを設定する関数
※1枚目のシート名は「出走馬一覧」として保存します。
"""
file_name = f"{title}.xlsx"
full_path = os.path.join(path, file_name)
sheet_name = "出走馬一覧"
df = convert_df_numeric_full(df)
df.to_excel(full_path, index=False, sheet_name=sheet_name)
wb = load_workbook(full_path)
wb.properties.title = title
wb.save(full_path)
print(f"データを {sheet_name} シートに保存し、ブックのタイトルを '{title}' に設定しました: {full_path}")
def get_race_id(url):
"""
URL 中の "race_id=" の後ろの数字部分を抽出して返す
"""
m = re.search(r"race_id=(\d+)", url)
if m:
return m.group(1)
return None
def parse_oikiri_table(soup):
"""
オイキリ画面の評価テーブル(id="All_Oikiri_Table")から、
各馬の評価情報(セル4: 評価コメント、セル5: 評価ランク)を抽出し、
馬名をキーとした辞書を返す。
※1行目はヘッダー行として除外し、2行目以降のデータ行を処理します。
返却値は、 {馬名: (評価コメント, 評価ランク)} となります。
"""
table = soup.find("table", id="All_Oikiri_Table")
if not table:
print("オイキリテーブルが見つかりませんでした。")
return {}
tbodies = table.find_all("tbody")
if tbodies:
tbody = tbodies[0]
else:
tbody = table
rows = tbody.find_all("tr")
if not rows:
print("オイキリテーブル内に行が見つかりませんでした。")
return {}
if rows[0].find_all("th"):
data_rows = rows[1:]
else:
data_rows = rows
evaluation_dict = {}
for row in data_rows:
cells = row.find_all("td")
if len(cells) < 7:
continue
horse_name_tag = cells[3].find("a")
if not horse_name_tag:
continue
horse_name = horse_name_tag.text.strip()
eval_comment = cells[4].get_text(strip=True)
eval_rank = cells[5].get_text(strip=True)
evaluation_dict[horse_name] = (eval_comment, eval_rank)
return evaluation_dict
def main():
# 例として、race_id の先頭10桁 "2025060207" を固定し、下2桁を09~09にループします
BASE_RACE_ID_PREFIX = "2025050206"
for num in range(1~12, 2~12):
race_id = BASE_RACE_ID_PREFIX + f"{num:02d}"
race_url = f"https://race.netkeiba.com/race/shutuba.html?race_id={race_id}"
print(f"\n====================\n現在処理中のレース: {race_id}\nURL: {race_url}\n====================")
soup = fetch_race_data(race_url)
if soup:
race_title = get_race_title(soup)
additional_info = get_additional_race_info(soup)
# レース番号を取得し、ファイル名の先頭に追加する
race_num = get_race_number(soup)
if race_num:
full_title = f"{race_num} {race_title}"
else:
full_title = race_title
df_race = parse_race_table(soup)
if df_race is not None:
new_columns = ["レース名", "場名", "芝・ダート", "距離", "天候", "馬場"]
for col in new_columns:
df_race[col] = additional_info[col]
cols = list(df_race.columns)
if "枠" in cols:
new_order = []
for col in cols:
if col == "枠":
new_order.extend(new_columns)
new_order.append(col)
elif col in new_columns:
continue
else:
new_order.append(col)
df_race = df_race[new_order]
else:
df_race = df_race[new_columns + cols]
df_race.rename(columns={"馬名": "馬", "馬名_リンク": "馬_リンク"}, inplace=True)
# 「性齢」列が存在する場合、「性」と「齢」に分割する
if "性齢" in df_race.columns:
idx = df_race.columns.get_loc("性齢")
df_race.insert(idx, "性", df_race["性齢"].astype(str).str[0])
df_race.insert(idx+1, "齢", df_race["性齢"].astype(str).str[1:])
df_race.drop("性齢", axis=1, inplace=True)
if "馬体重(増減)" in df_race.columns:
def extract_increase(s):
if isinstance(s, str):
m = re.search(r'\((.*?)\)', s)
return m.group(1) if m else ""
return ""
df_race["増減"] = df_race["馬体重(増減)"].apply(extract_increase)
df_race["馬体重(増減)"] = df_race["馬体重(増減)"].apply(lambda s: re.sub(r'\(.*?\)', '', s).strip() if isinstance(s, str) else s)
df_race.rename(columns={"馬体重(増減)": "馬体重"}, inplace=True)
cols = list(df_race.columns)
if "馬体重" in cols:
idx = cols.index("馬体重")
if "増減" in cols:
cols.remove("増減")
cols.insert(idx+1, "増減")
df_race = df_race[cols]
# Excel に保存(ブック名にレース番号が先頭に入る)
save_excel_with_title(df_race, OUTPUT_PATH, full_title)
file_path = os.path.join(OUTPUT_PATH, f"{full_title}.xlsx")
df_all = pd.read_excel(file_path, sheet_name="出走馬一覧")
url_column, horse_name_column = "馬_リンク", "馬"
if url_column not in df_all.columns or horse_name_column not in df_all.columns:
print(f"列 '{url_column}' または '{horse_name_column}' が見つかりませんでした。")
continue
all_horses_data = []
bloodline_dict = {}
for idx, row in df_all.iterrows():
horse = row[horse_name_column]
url = row[url_column]
if pd.isna(url) or url == "" or pd.isna(horse) or str(horse).strip() == "":
print("URLまたは馬名が空です。スキップします。")
continue
print(f"現在処理中の馬: {horse}")
result = get_horse_data(url)
if result is not None:
horse_data_df, bloodline = result
if "馬体重" in horse_data_df.columns:
def extract_increase(s):
if isinstance(s, str):
m = re.search(r'\((.*?)\)', s)
return m.group(1) if m else ""
return ""
horse_data_df["増減"] = horse_data_df["馬体重"].apply(extract_increase)
horse_data_df["馬体重"] = horse_data_df["馬体重"].apply(lambda s: re.sub(r'\(.*?\)', '', s).strip() if isinstance(s, str) else s)
cols = list(horse_data_df.columns)
if "馬体重" in cols:
idx_mt = cols.index("馬体重")
if "増減" in cols:
cols.remove("増減")
cols.insert(idx_mt+1, "増減")
horse_data_df = horse_data_df[cols]
horse_data_df.insert(0, '馬', horse)
horse_data_df = convert_df_numeric_full(horse_data_df)
all_horses_data.append(horse_data_df)
bloodline_dict[idx] = bloodline
print(f"{horse} の戦績データと血統データを取得しました。")
else:
print(f"{horse} のリンク先からデータを取得できませんでした。")
blood_cols = ["父", "父の父", "父の母", "母", "母の父", "母の母"]
for col in blood_cols:
df_all[col] = ""
for idx, row in df_all.iterrows():
bloodline_list = bloodline_dict.get(idx, [])
if len(bloodline_list) > 0:
df_all.at[idx, "父"] = bloodline_list[0]
if len(bloodline_list) > 1:
df_all.at[idx, "父の父"] = bloodline_list[1]
if len(bloodline_list) > 2:
df_all.at[idx, "父の母"] = bloodline_list[2]
if len(bloodline_list) > 3:
df_all.at[idx, "母"] = bloodline_list[3]
if len(bloodline_list) > 4:
df_all.at[idx, "母の父"] = bloodline_list[4]
if len(bloodline_list) > 5:
df_all.at[idx, "母の母"] = bloodline_list[5]
cols = list(df_all.columns)
if "厩舎_リンク" in cols:
trainer_link_column = "厩舎_リンク"
trainer_column = "厩舎"
for idx, row in df_all.iterrows():
url = row[trainer_link_column]
if pd.isna(url) or url == "":
continue
trainer_name = get_trainer_name(url)
if trainer_name:
df_all.at[idx, trainer_column] = trainer_name
print(f"厩舎 {trainer_name} を取得しました。")
else:
print(f"厩舎名の取得に失敗しました: {url}")
insert_at = cols.index("厩舎_リンク") + 1
remaining = [c for c in cols if c not in blood_cols]
new_order = remaining[:insert_at] + blood_cols + remaining[insert_at:]
df_all = df_all[new_order]
jockey_link_column = "騎手_リンク"
jockey_column = "騎手"
if jockey_link_column in df_all.columns:
for idx, row in df_all.iterrows():
url = row[jockey_link_column]
if pd.isna(url) or url == "":
continue
jockey_name = get_jockey_name(url)
if jockey_name:
df_all.at[idx, jockey_column] = jockey_name
print(f"騎手 {jockey_name} を取得しました。")
else:
print(f"騎手名の取得に失敗しました: {url}")
with pd.ExcelWriter(file_path, engine="openpyxl", mode="a", if_sheet_exists="replace") as writer:
df_all.to_excel(writer, sheet_name="出走馬一覧", index=False)
oikiri_url = f"https://race.netkeiba.com/race/oikiri.html?race_id={race_id}&rf=race_submenu"
print(f"オイキリURL: {oikiri_url}")
response = requests.get(oikiri_url, headers=HEADERS)
if response.status_code != 200:
print(f"オイキリページの取得に失敗しました。HTTPコード: {response.status_code}")
else:
soup_oikiri = BeautifulSoup(response.content, "html.parser")
eval_dict = parse_oikiri_table(soup_oikiri)
if not eval_dict:
print("オイキリから評価データを取得できませんでした。")
else:
comment_list = []
rank_list = []
for idx, row in df_all.iterrows():
horse = str(row.get("馬", "")).strip()
if horse in eval_dict:
raw_comment, raw_rank = eval_dict[horse]
comment = re.sub(r'[A-Za-z]', '', raw_comment)
rank = ''.join(re.findall(r'[A-Za-z]', raw_rank))
else:
comment = ""
rank = ""
comment_list.append(comment)
rank_list.append(rank)
df_all["調教タイム評価‗コメント"] = comment_list
df_all["調教タイム評価‗ランク"] = rank_list
cols = list(df_all.columns)
if "人気" in cols:
idx_pop = cols.index("人気") + 1
for col in ["調教タイム評価‗コメント", "調教タイム評価‗ランク"]:
if col in cols:
cols.remove(col)
cols.insert(idx_pop, "調教タイム評価‗コメント")
cols.insert(idx_pop+1, "調教タイム評価‗ランク")
df_all = df_all[cols]
else:
print("「人気」列が見つからなかったため、評価関連の列は末尾に配置されます。")
with pd.ExcelWriter(file_path, engine="openpyxl", mode="a", if_sheet_exists="replace") as writer:
df_all.to_excel(writer, sheet_name="出走馬一覧", index=False)
print("調教タイム評価のコメントとランクを追加しました。")
if all_horses_data:
combined_data = pd.concat(all_horses_data, ignore_index=True)
if "距離" in combined_data.columns:
distance_idx = combined_data.columns.get_loc("距離")
combined_data.insert(distance_idx, "芝・ダート",
combined_data["距離"].apply(lambda x: x[0] if isinstance(x, str) and x and x[0] in {"芝", "ダ"} else ""))
combined_data["距離"] = combined_data["距離"].apply(lambda x: re.sub(r'^[芝ダ]', '', x) if isinstance(x, str) else x)
if "開催" in combined_data.columns:
combined_data["開催"] = combined_data["開催"].apply(
lambda x: re.sub(r'^\d+|\d+$', '', x) if isinstance(x, str) else x
)
combined_data = convert_df_numeric_full(combined_data)
# キーワードに部分一致する列名を削除する
keys_to_drop = ["馬場指数", "タイム指数", "厩舎コメント", "備考", "勝ち馬(2着馬)", "賞金","映像"]
drop_list = [col for col in combined_data.columns if any(key in col for key in keys_to_drop)]
combined_data.drop(columns=drop_list, inplace=True)
with pd.ExcelWriter(file_path, engine="openpyxl", mode="a", if_sheet_exists="replace") as writer:
combined_data.to_excel(writer, sheet_name="全馬戦績", index=False)
print("全ての馬の戦績データが '全馬戦績' シートに保存されました。")
else:
print("データを取得できた馬がありませんでした。")
else:
print("レースページの取得に失敗しました。")
if __name__ == "__main__":
main()
保存場所の設定
最初に保存する場所を設定する必要があります。
OUTPUT_PATH = r"C:\Users\User\Desktop"
太字の部分に保存したいフォルダのパスを入れてください。
取得したいレースの設定
途中以下のコードがありますが、netkeibaの取得したいレースのURLの数字にコピペして書き換えてください。
例えば、https://race.netkeiba.com/race/shutuba.html?race_id=202505020611のid=の後ろの数字をBASE_RACE_ID_PREFIX = “2025050206“にコピペしてください。
その後、数字の後ろ二つを削除してください。上のレースURLでだと、BASE_RACE_ID_PREFIX = “202505020611“の11の部分を削除。
次に、for num in range(1~12, 2~12):の数字を入力してください。
11Rを取得したい場合は、for num in range(11, 12):と入力してください。
1R~12Rを取得したい場合は、for num in range(1, 13):
def main():
# 例として、race_id の先頭10桁 "2025060207" を固定し、下2桁を09~09にループします
BASE_RACE_ID_PREFIX = "2025050206"
for num in range(1~12, 2~12):
race_id = BASE_RACE_ID_PREFIX + f"{num:02d}"
race_url = f"https://race.netkeiba.com/race/shutuba.html?race_id={race_id}"
print(f"\n====================\n現在処理中のレース: {race_id}\nURL: {race_url}\n====================")
実行結果(例)
レース名 場名 芝・ダート 距離 天候 馬場 枠 馬番 馬 性 齢 斤量 騎手 厩舎 馬体重 増減 "オッズ
更新" 人気 調教タイム評価‗コメント 調教タイム評価‗ランク
NHKマイルC 東京 芝 1600 晴 良 1 1 モンドデラモーレ 牡 3 57 戸崎圭太 千葉直人 19 8 動き軽快 B
NHKマイルC 東京 芝 1600 晴 良 1 2 ショウナンザナドゥ 牝 3 55 池添謙一 松下武士 80.3 14 上積無し C
AIにどの馬が勝つのか聞いてみる
chatgptやgemini、claudeなどに取得したエクセルをコピペ、アップロード等をして聞いてみてください。
聞き方は人によってさまざまあると思いますので、お任せします。
注意点
- 商用利用はNG:netkeibaはスクレイピングの商用利用を禁止しています。個人の学習・研究目的にとどめましょう。
- アクセスしすぎ注意:短時間で大量アクセスするとブロックされる可能性も。必要に応じて
time.sleep()
を入れてください。
まとめ
今回は、Pythonでnetkeibaから競走馬の戦績をスクレイピングする方法を、コード付きで分かりやすく解説しました。
データが取れれば、以下のような分析も可能になります:
- 騎手ごとの勝率ランキング
- 距離別の得意不得意
- 馬場状態との相性 など
「Python × 競馬」はとても面白い分野ですので、ぜひチャレンジしてみてください!
コメント