3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ボートレースの結果のファイルをスクレイピングでとってくる

Last updated at Posted at 2022-05-11

目的

ボートレースの機械学習を行うため以下のリンクに載っている各種ダウンロードの競争成績ダウンロード番組表ダウンロードの二つのデータを集めます。

ただこの二つをダウンロードするためには開催された年、月、日付け手動で選択する必要があり、数年分のデータをダウンロードするとなるとかなり手間になってします。
そこでダウンロードする作業をseleniumを使って自動化してみます。
またダウンロードするデータはlzhという圧縮ファイル形式になっているので解凍して別のフォルダの保存する作業も自動化します。

実装環境

  • Windows10
  • Anaconda
  • Python=3.7.13
  • VScode

環境構築

今回環境構築ではAnacondaをつかって行います。

Anaconda Prompt
conda create -n selenium
Anaconda Prompt
conda activate selenium

seleniumという名前の仮想環境を構築し、conda activateで仮想環境を有効にします。
次に必要なライブラリをインストールします。

追記:seleniumのバージョン4.3.0からfind_element_by系統のメソッドが削除されたのでインストールする際は4.3.0以前のものをバージョン指定して下さい。

Anaconda Prompt
conda install selenium
Anaconda Prompt
pip install lhafile
Anaconda Prompt
pip install webdriver_manager

今回使うライブラリはselenium lhafile webdriver_managerの3つです。selenium以外はcondaにはなかったので、pipを使ってインストールしています。

ちなみに...

  • seleniumはPythonで自動化やスクレイピングをするためのライブラリです。
  • lhafilelzh形式のファイルの読み書きをするためのライブラリです。
  • webdriver_managerはChromeを最新バージョンにしてから立ち上げるライブラリです。これを使うことで、バージョンが異なる際に生じるエラーを回避することができます。詳しくはこのリンクを見てみてください。

環境構築は以上になります。

コーディング

今回はファイルのダウンロードをする処理と、ファイルを解凍する処理をそれぞれ関数にして作ります。

ファイルダウンロード編

ここでは引数をダウンロード先のパス ダウンロード元のサイトのURL 取得するデータの期間(月毎) の3つの関数を作っていきます。
処理全体のコードは以下のようになります。

scraping.py
# 必要なモジュールのインポート
from selenium import webdriver
import time
from selenium.webdriver.support.select import Select
from webdriver_manager.chrome import ChromeDriverManager

def kyoutei_data_download(download, url, month):
    options = webdriver.ChromeOptions()
    options.add_experimental_option("prefs", {"download.default_directory": download })
    browser = webdriver.Chrome(ChromeDriverManager().install(), chrome_options=options)
    browser.implicitly_wait(3)
    url = url
    #urlからwebページに移る
    browser.get(url)
    #処理を3秒間止める
    time.sleep(3)

    for i in range(month):
        #HTMLのframeを選択して移る
        browser.switch_to.frame('menu')

        #ダウンロードしたい月の項目を選択
        dropdown = browser.find_element_by_name('MONTH')
        select = Select(dropdown)


        #ご覧になりたい日付けのところを任意のところに選択できる
        select.select_by_index(i+1)
        #デフォルトのフレームに移る
        browser.switch_to.default_content()
        #3秒間処理を止める
        time.sleep(3)
        #JYOUというnameをしているframeに移る
        browser.switch_to.frame("JYOU")
        
        #日付ごとに選択してダウンロードする処理
        for j in range(len(browser.find_elements_by_xpath("//*[@type='radio']"))):     
            time.sleep(6)
            #radioボタンの選択(日にちの選択)
            browser.find_elements_by_xpath("//*[@type='radio']")[j].click()

            browser.find_elements_by_xpath("//*[@value='ダウンロード開始']")[0].click()

        browser.switch_to.default_content()    

        time.sleep(2)

    browser.close()

各コードの処理内容

各コードで何をしているのかを解説してみます。

Chromeの設定と立ち上げ処理

options = webdriver.ChromeOptions()

ここでは立ち上げるブラウザの設定を行うためのインスタンスを生成しています。

options.add_experimental_option("prefs", {"download.default_directory": download })

ここではブラウザ上でダウンロードしたデータの保存先を設定しています。
ダウンロード先のパスである引数のdawnloadを指定しているのでユーザーが指定したフォルダに保存することができます。

browser = webdriver.Chrome(ChromeDriverManager().install(), chrome_options=options)

ここでは立ち上げるChromeの実行ファイルと先ほど設定した内容を反映させています。
実行ファイルの引数にChromeDriverManager().install()を指定することで、常に最新バージョンのChromeの実行ファイルで立ち上げることができます。
詳しくは以下のリンクを参考にしてください。

browser.implicitly_wait(3)

ここでは要素が見つかるまでの待機時間を指定しています。
詳しくは以下のサイトを参考にしてみてください。

for文内の処理

for文の中では月を選択した後に日にちを一日ずつ選択したらダウンロードボタンを押してダウンロードする処理を行っています。
2か月以上のデータを取得する場合は、すべての日にちのダウンロードが終わったらひとつ前の月を選択して同様の処理を行っています。
コードの中にはサイト内で使われている単語があったりするのでF12などを押してデベロッパーツールを起動してサイトのマークダウンを確認しながら解説していきます。

今回は番組表のサイトのマークダウンを見ていきます。(結局競争成績も同じ作りでした)
スクリーンショット 2022-05-11 103734.png

マークダウンを見ていくと二つのHTMLファイルがframeタグで指定されています。name属性はそれぞれmenuJYOUです。操作していくとわかりますが、memuのHTMLファイルは月指定する場所になります。JYOUは初期の状態では何もありませんが月を選択した後に出てくる日付け選択の項目の場所になります。
今の状態のままだと二つのHTMLファイルがある状態なので選択もクリックもできないので、それぞれのファイルに移動する必要があります。移動せずに操作を行うとエラーが出ます。

browser.switch_to.frame('menu')

switch_to.frameメソッドを使うことで指定したname属性のHTMLファイルのフレームに移動します。移動したらようやく選択項目を操作することができます。実装してみて一番わかりずらいエラーでした。以下のリンクで同じエラーの方がいたので参考までにしてください。

dropdown = browser.find_element_by_name('MONTH')
select = Select(dropdown)

ここでは月の選択項目を選ぶ処理をしています。今回はMONTHというname属性を指定しています。

select.select_by_index(i+1)

月の項目を選択したら実際どの月を選ぶのかを指定しています。iはfor文の中にあるのでユーザーが指定した期間だけ選択することになります。i+1としているのは、月の選択項目の初めの選択肢がデフォルトで設定されている(月を設定してください)となっているためそれを省くためです。
これで月の選択は以上です。

browser.switch_to.default_content()
time.sleep(3)

switch_to.default_content()を使いひとつ前のフレームに戻ります。これを行わないと日付け選択の部分であるJYOUに移動することができません。time.sleep(3)で処理を3秒間止めています。月を指定してから日付けが表示されるまで若干時間がかかるので表示されるまでの待機時間として処理を加えています。

browser.switch_to.frame("JYOU")

日付けの項目が表示されたらname属性がJYOUのフレーム移動します。

ここからは日にちごとにダウンロードを行っていきます。

len(browser.find_elements_by_xpath("//*[@type='radio']"))

for文で範囲指定する部分では上記のようなコードを指定しています。ここでは端的に言えばラジオボタンが何個あるのかを数えています。ラジオボタンの数だけ処理を繰り返すためこのような処理をしています。
browser.find_elements_by_xpath("//*[@type='radio']")この部分でラジオボタンのリストを取得することができます。
以下を参考です。

time.sleep(6)
browser.find_elements_by_xpath("//*[@type='radio']")[j].click()
browser.find_elements_by_xpath("//*[@value='ダウンロード開始']")[0].click()

ここでは実際に日付けを1日から選択してダウンロードボタンを押してダウンロードをしています。ラジオボタンの選択はリストのインデックス番号を指定することで選択できます。ダウンロードボタンは一つの項目しかないのでインデックス番号は0で固定されています。
time.sleep(6)で処理を6秒間止めていますが、これはダウンロードボタンを押してからダウンロードされるまでの間にラグがあるため日付け選択の処理の間に処理を加えています。

browser.switch_to.default_content()    
time.sleep(2)

最後にひとつ前のフレームに移動して処理を2秒間止めて、browser.close()でウェブドライバを閉じて一連の処理を終了します。
以上がファイルをダウンロードする関数の解説でした。

ファイル解凍編

lzhファイル解凍処理は以下のリンクを参考にして作ります。
詳しい内容については以下のリンクを参考にしてください。

ここでは引数が解凍したいファイルのパス 解凍したファイルを保存するファイルのパス の関数を作っていきます。

scraping.py
# 必要なモジュールのインポート
import re
import lhafile
import os

def data_kaitou(kyoutei_download_folder,kaitou_folder):
    os.makedirs(kaitou_folder, exist_ok = True)
    lzh_file_list = os.listdir(kyoutei_download_folder)
    for lzh_file_name in lzh_file_list:

        file = lhafile.Lhafile(os.path.join(kyoutei_download_folder, lzh_file_name))

        # 解凍したファイルの名前を取得
        info = file.infolist()
        name = info[0].filename

        # 解凍したファイルの保存
        open(os.path.join(kaitou_folder ,name), "wb").write(file.read(name))

まとめる

上記で作った二つの関数をまとめます。
ここではinput関数でダウンロード先のフォルダのパス 解凍したファイルの保存先のパス 集めたい競艇データのURL 集めるデータの期間(半角)を受け取るようにします。
処理が終わったらすべての処理が完了しましたという文字を出力して終了です。

scraping.py
if __name__ == '__main__':
    #必要な項目の入力
    download_folder = input('ダウンロードするファイルのパスを指定してください')
    kaitou_folder = input("解凍したファイルの保存先のパスを指定してください")
    url = input("集めたい競艇データのURLを指定してください")
    manth = int(input("何か月分のデータを集めるか指定してください(半角)"))

    #データのダウンロード処理
    kyoutei_data_download(download_folder, url, manth)

    #圧縮ファイルの解凍処理
    data_kaitou(download_folder, kaitou_folder)

    print('すべての処理が完了しました')

実行

今回作成したプログラムを実際に動かしてみます。
URLは以下のリンクをコピーして使ってみてください。
番組表のURL:http://www1.mbrace.or.jp/od2/B/dindex.html
競争成績のURL:http://www1.mbrace.or.jp/od2/K/dindex.html
パスの入力はAnacondaPromptに指定のファイルをドラックアンドドロップする方法が簡単です。

  • 初めに作成したseleniumという仮想環境を有効にする
Anaconda Prompt
conda activate selenium
  • Pythonの実行ファイルがある階層までディレクトリ移動する。
Anaconda Prompt
cd ...
cd ...
cd ...
  • Pythonファイルを実行
Anaconda Prompt
python scraping.py

入力項目に値を入力(パス入力はファイルをドラックアンドドロップ)
すべての処理が終わったら以下のような画面になります。
スクリーンショット 2022-05-11 012112.png

保存先のファイル保存されていたら成功です。
2022年5月分のデータをダウンロードした結果以下のように保存することができました。

ダウンロードしたファイル↓
スクリーンショット 2022-05-11 012217.png

解凍したファイル↓
スクリーンショット 2022-05-11 012247.png

最後に

見ていただいてありがとうございました。実際に実装してもらうとわかりますが10年分のデータを集めようとすると30分以上はかかってしまいます。time.sleepがボトルネックになっている感はあります。すべて手探りで調整したものなので最適ではないと思われます。サイトのほうでも選択してから日付けが表示されるまでに時間かかっているので手作業でやるとストレスがかかります。一度実行したらあとは勝手にやってくれる自動化のほうがストレスフリーなので結果的に自動化してよかったです。

3
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?