新着記事一覧

IT・プログラミング Python

2024/4/21

【Python】Pythonでの自然言語処理(日本語)

こちらの記事では、自然言語処理についてまとめています。 Contents1 自然言語処理とは?2 自然言語処理の実装 自然言語処理とは? 自然言語とは、日本語や英語のような自然発生的に生まれた言語です。日常的に使用している自然言語について、言葉が持つ意味を解析し処理します。活用事例としては、チャットボット、音声認識AI、文字認識(手書き文字をカメラが認識し文字データへと変換する技術)、検索エンジン、翻訳、感情分析、文章要約などがあります。 文章の意味を機械に理解させるには、単語分割を行う必要があります。手 ...

続きを読む

IT・プログラミング Python

2024/4/8

【Python】統計学的モデル 時系列分析の実装

こちらの記事では、時系列分析の実装についてまとめています。 Contents1 時系列分析とは?2 定常性とは?3 時系列データの分析方法4 SARIMAモデルの実装 時系列分析とは? 時系列分析では、時間経過とともに変化する時系列データを扱います。時系列データの例としては、毎時間の気温、株価の推移等がこれにあたります。売上予測、来店者予測等、ビジネスにおいて重要な分析技術です。 時系列データには以下3種類があります。 1.トレンドデータの長期的な傾向。時間の経過とともに値が上昇・下降している時系列データ ...

続きを読む

IT・プログラミング Python

2024/4/1

【Python】主成分分析の実装

こちらの記事では、主成分分析を実装していきます。 Contents1 主成分分析とは2 主成分分析の手順について 主成分分析とは 主成分分析とは、次元削減を行う時によく使う手法です。例えば、いくつかの特徴量があるデータを2次元データに変換すると、できるだけ情報を保ったまま2軸での描画が可能になり、全てのデータを見やすく示すことができます。 主成分分析の実用例として、製品やサービスのスコアリングや比較(1次元に圧縮)、データの可視化(2,3次元に圧縮)、回帰分析の前処理などが挙げられます。 主成分分析の手順 ...

続きを読む

IT・プログラミング Python

2024/4/1

【Python】カーネル主成分分析の実装

こちらの記事では、カーネル主成分分析の手順についてまとめています。 Contents1 カーネル主成分分析とは2 カーネル主成分分析の実装 カーネル主成分分析とは 回帰分析等、機械学習の多くのアルゴリズムは線形分離できるデータが与えられることを前提としていますが、現実的には線形分離できないデータ、つまり非線形分離する必要があるデータがほとんどです。非線形分離する必要があるデータに対処できるのが、カーネル主成分分析(kernel PCA)です。 カーネル主成分分析ではN×M(データの数×特徴の種類)のデータ ...

続きを読む

IT・プログラミング Python

2024/3/29

【Python】機械学習(教師なし) クラスタリング DBSCANの実装

こちらの記事では、機械学習(教師なし)の非階層的クラスタリング DBSCAN法についてまとめていきます。 Contents1 クラスタリングとは2 DBSCANでの実装 クラスタリングとは データをクラスター(塊)に分割する操作のことです。クラスタリングの中でも階層的クラスタリングと、非階層的クラスタリングの2種に分けられています。 1.階層的クラスタリングデータの中から最も似ている組み合わせを探し出して、順番にクラスターにしていく方法です。最終的に全データをまとめるクラスターに行くつけば終了です。 2. ...

続きを読む

IT・プログラミング Python

2024/3/28

【Python】機械学習(教師なし) クラスタリング k-meansの実装

こちらの記事では、機械学習(教師なし)の非階層的クラスタリング k-means法についてまとめていきます。 Contents1 クラスタリングとは2 k-means法での実装 クラスタリングとは データをクラスター(塊)に分割する操作のことです。クラスタリングの中でも階層的クラスタリングと、非階層的クラスタリングの2種に分けられています。 1.階層的クラスタリングデータの中から最も似ている組み合わせを探し出して、順番にクラスターにしていく方法です。最終的に全データをまとめるクラスターに行くつけば終了です。 ...

続きを読む

IT・プログラミング Python

2024/3/28

【Python】機械学習のデータ前処理 外れ値の扱いについて

外れ値の処理についてまとめています。 Contents1 外れ値とは?2 外れ値の検知方法について 外れ値とは? 外れ値とは、他のデータと著しく乖離したデータのことを指します。データ内に外れ値が混在していると、分析結果に影響を及ぼしたり、機械学習モデルの学習過程で影響がでてしまい学習が進みにくくなる、などの影響が出てしまいます。 外れ値の検知方法について 外れ値の検知方法について、可視化、LOF(Local Outlier Factor )、Isolation Forestの3つの手法について紹 ...

続きを読む

IT・プログラミング Python

【Python】Netkeibaから競馬データをスクレイピング

競馬予想モデルを作成してみたく、機械学習用のデータをNetkeibaさん(https://www.netkeiba.com/)からスクレイピングさせて頂きました。

※学習初学者の勉強のアウトプットですので、もしコードや解釈に間違い等あればご指摘頂けますと幸いです。
メモも兼ねて、各コードなるべく詳しく説明をつけるようにしています。

スクレイピングの前に

スクレイピングに取り掛かる前に、まずは利用規約を一読しました。
スクレイピングNGという表現はなく、またデータも私的利用であれば大丈夫そうです。サイトによっては明確にスクレイピングNGとしているサイトもありますので、この点要注意です。

スクレイピングのコード

では、スクレイピングしたコードを記載していきます。
2015-2022年を訓練データ、2023年をテストデータとしたく、9年分のデータをスクレイピングしました。一気にデータ取得するとかなりの時間を要しますので、以下コード内では3年分(2015-2017年)のデータをひとまず取得しました。年のリストを変更し、3回実施致ました。3年分のデータ取得で4時間半ほどかかりました。

#Netkeibaからスクレイピングを行う
#必要なモジュールのインポート
import requests
from bs4 import BeautifulSoup
import urllib
import pandas as pd
import csv
import re

#2015-2017年のデータを取る
#解析するURLを入れるリストを作成し、URLを保存していく
urls = []
for y in ["2015","2016","2017"]:#年の指定
  for l in ["01","02","03","04","05","06","07","08","09","10"]:#競馬場番号
    for h in ["01","02","03","04","05","06"]:#開催回数
      for d in ["01","02","03","04","05","06","07","08","09","10","11","12"]:#開催日
        for r in ["01","02","03","04","05","06","07","08","09","10","11","12"]:#レース番号
              urls.append(f"https://db.netkeiba.com/race/{y}{l}{h}{d}{r}/")

#スクレイピングしたデータを格納するための空のデータフレームを作っておく
result_df = pd.DataFrame()

#中にはデータが存在しないURLもあるため、try/exceptにてエラーが出た場合処理を飛ばすようにする
for url in urls:
  try:
    html = requests.get(url)
    soup = BeautifulSoup(html.content, "html.parser") #textだと文字化けするので、html.contentに変更する

#テーブル以外の要素の抽出を行う
    race_title = soup.find(class_="racedata fc").find("h1")#レース名
    ground = soup.find(class_="racedata fc").find("span").contents[0][0]#芝/ダ/障 障害は分析することはないので、後々の分析からははずす
    turn = soup.find(class_="racedata fc").find("span").contents[0][1]#左/右/芝 障害の場合は芝と表示されるが、障害は分析対象外にするので問題ない
    distance = re.findall(r'\d{4}', soup.find(class_="racedata fc").find("span").contents[0])[0]#距離 正規表現で数字4桁を探す
    weather = re.findall(r'天候\s*:\s*([^\/]+)', soup.find(class_="racedata fc").find("span").contents[0].replace("\xa0",""))[0]#天候
    ground_condition =  re.findall(r'良|稍重|重|不良', soup.find(class_="racedata fc").find("span").contents[0])[0]#馬場状況
    year = re.findall(r'(\d{4})',soup.find(class_="smalltxt").contents[0])[0]#開催年
    date = re.findall(r'(\d{1,2}月\d{1,2}日)',soup.find(class_="smalltxt").contents[0])[0]#開催日
    location = re.findall(r'\d+回(..)',soup.find(class_="smalltxt").contents[0])[0]#場所

#テーブル要素の抽出を行う
    table = soup.find("table")

#テーブルデータの保存先を作る
#テーブルデータの最初のtr(ヘッダー)を除く行をすべて解析、その中にあるtd要素を全てテキストで抽出し、かつテーブル以外の要素と組み合わせ、保存先に保存
    table_data =[]
    for row in table.find_all("tr")[1:]:
      new_row_data = [year] + [date] + [location] +  [race_title.text] + [ground] + [turn] + [distance] + [weather] + [ground_condition] + [cell.get_text(strip=True) for cell in row.find_all(['td'])]
      table_data.append(new_row_data)

    df = pd.DataFrame(table_data)
    result_df = pd.concat([result_df, df], ignore_index=True)

  except Exception as e:
    pass

# CSVファイルとして保存
result_df.to_csv("horse_db_2015_2017.csv", encoding="utf-8-sig")

解説

#Netkeibaからスクレイピングを行う
#必要なモジュールのインポート
import requests
from bs4 import BeautifulSoup
import urllib
import pandas as pd
import csv
import re

#2015-2017年のデータを取る
#解析するURLを入れるリストを作成し、URLを保存していく
urls = []
for y in ["2015","2016","2017"]:#年の指定
  for l in ["01","02","03","04","05","06","07","08","09","10"]:#競馬場番号
    for h in ["01","02","03","04","05","06"]:#開催回数
      for d in ["01","02","03","04","05","06","07","08","09","10","11","12"]:#開催日
        for r in ["01","02","03","04","05","06","07","08","09","10","11","12"]:#レース番号
              urls.append(f"https://db.netkeiba.com/race/{y}{l}{h}{d}{r}/")

まず、必要なライブラリをインポートし、その後解析するURLを取得してappendでリスト(urls)に保存していきます。
netkeibaのURLのロジックについては、こちらのサイトを大いに参考にさせて頂きました。ありがとうございます!

#スクレイピングしたデータを格納するための空のデータフレームを作っておく
result_df = pd.DataFrame()

#中にはデータが存在しないURLもあるため、try/exceptにてエラーが出た場合処理を飛ばすようにする
for url in urls:
  try:
    html = requests.get(url)
    soup = BeautifulSoup(html.content, "html.parser") #textだと文字化けするので、html.contentに変更する

抽出した情報を保存するために、まずは空のデータフレームを準備しておきます。
次に、urlsの中に格納したURLを解析してHTMLの情報を読み込んで行きますが、中にはurl内にデータが存在しないものもあり、その場合エラーで実行が止まってしまいます。try/Exceptを入れてエラーがでた時の処理を飛ばします。

上記で読み込んだHTMLから該当する情報を抽出していきます。
黄色の部分、赤枠のtable部分に分けて抽出していきました。

#テーブル以外の要素の抽出を行う
    race_title = soup.find(class_="racedata fc").find("h1")#レース名
    ground = soup.find(class_="racedata fc").find("span").contents[0][0]#芝/ダ/障 障害は分析することはないので、後々の分析からははずす
    turn = soup.find(class_="racedata fc").find("span").contents[0][1]#左/右/芝 障害の場合は芝と表示されるが、障害は分析対象外にするので問題ない
    distance = re.findall(r'\d{4}', soup.find(class_="racedata fc").find("span").contents[0])[0]#距離 正規表現で数字4桁を探す
    weather = re.findall(r'天候\s*:\s*([^\/]+)', soup.find(class_="racedata fc").find("span").contents[0].replace("\xa0",""))[0]#天候
    ground_condition =  re.findall(r'良|稍重|重|不良', soup.find(class_="racedata fc").find("span").contents[0])[0]#馬場状況
    year = re.findall(r'(\d{4})',soup.find(class_="smalltxt").contents[0])[0]#開催年
    date = re.findall(r'(\d{1,2}月\d{1,2}日)',soup.find(class_="smalltxt").contents[0])[0]#開催日
    location = re.findall(r'\d+回(..)',soup.find(class_="smalltxt").contents[0])[0]#場所

まず、黄色枠部分の情報で分析に使うであろう情報を抽出し、各変数に格納していきます。
久しぶりの正規表現に苦労しました。適宜Chat GPT先生にお助け頂きました。

#テーブルデータの保存先を作る
#テーブルデータの最初のtr(ヘッダー)を除く行をすべて解析、その中にあるtd要素を全てテキストで抽出し、かつテーブル以外の要素と組み合わせ、保存先に保存
    table_data =[]
    for row in table.find_all("tr")[1:]:
      new_row_data = [year] + [date] + [location] +  [race_title.text] + [ground] + [turn] + [distance] + [weather] + [ground_condition] + [cell.get_text(strip=True) for cell in row.find_all(['td'])]
      table_data.append(new_row_data)

    df = pd.DataFrame(table_data)
    result_df = pd.concat([result_df, df], ignore_index=True)

  except Exception as e:
    pass

次に、赤枠のtable部分の情報を抽出していきます。for row in table.find_all("tr")[1:]:とすることにより、カラム名を除外することができます。
かつ上述で抽出した黄色枠部分の情報を加えて、データフレームを作ります。

# CSVファイルとして保存
result_df.to_csv("horse_db_2015_2017.csv", encoding="utf-8-sig")

最後に抽出した情報をcsvに保存し、試しにダウンロードして中身を確認!バッチリです!
3年分抽出で4時間半ほどかかりましたが、ファイルが出来上がった時にはPythonのすごさを改めて感じました(涙)

  • この記事を書いた人

Haru

会社員。2回目の育休から仕事復帰。
職種は海外営業。
育休の過ごし方、育児、家事、プログラミング勉強について発信しています。

-IT・プログラミング, Python
-, ,