目的
ボートレースの機械学習を行うため以下のリンクに載っている各種ダウンロードの競争成績ダウンロードと番組表ダウンロードの二つのデータを集めます。
ただこの二つをダウンロードするためには開催された年、月、日付け
を手動で選択する必要があり、数年分のデータをダウンロードするとなるとかなり手間になってします。
そこでダウンロードする作業をseleniumを使って自動化してみます。
またダウンロードするデータはlzh
という圧縮ファイル形式になっているので解凍して別のフォルダの保存する作業も自動化します。
実装環境
- Windows10
- Anaconda
- Python=3.7.13
- VScode
環境構築
今回環境構築ではAnacondaをつかって行います。
conda create -n selenium
conda activate selenium
selenium
という名前の仮想環境を構築し、conda activate
で仮想環境を有効にします。
次に必要なライブラリをインストールします。
追記:seleniumのバージョン4.3.0からfind_element_by
系統のメソッドが削除されたのでインストールする際は4.3.0以前のものをバージョン指定して下さい。
conda install selenium
pip install lhafile
pip install webdriver_manager
今回使うライブラリはselenium
lhafile
webdriver_manager
の3つです。selenium
以外はcondaにはなかったので、pipを使ってインストールしています。
ちなみに...
-
selenium
はPythonで自動化やスクレイピングをするためのライブラリです。 -
lhafile
はlzh
形式のファイルの読み書きをするためのライブラリです。 -
webdriver_manager
はChromeを最新バージョンにしてから立ち上げるライブラリです。これを使うことで、バージョンが異なる際に生じるエラーを回避することができます。詳しくはこのリンクを見てみてください。
環境構築は以上になります。
コーディング
今回はファイルのダウンロードをする処理と、ファイルを解凍する処理をそれぞれ関数にして作ります。
ファイルダウンロード編
ここでは引数をダウンロード先のパス
ダウンロード元のサイトのURL
取得するデータの期間(月毎)
の3つの関数を作っていきます。
処理全体のコードは以下のようになります。
# 必要なモジュールのインポート
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などを押してデベロッパーツールを起動してサイトのマークダウンを確認しながら解説していきます。
今回は番組表のサイトのマークダウンを見ていきます。(結局競争成績も同じ作りでした)
マークダウンを見ていくと二つのHTMLファイルがframe
タグで指定されています。name属性はそれぞれmenu
とJYOU
です。操作していくとわかりますが、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
ファイル解凍処理は以下のリンクを参考にして作ります。
詳しい内容については以下のリンクを参考にしてください。
ここでは引数が解凍したいファイルのパス
解凍したファイルを保存するファイルのパス
の関数を作っていきます。
# 必要なモジュールのインポート
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
集めるデータの期間(半角)
を受け取るようにします。
処理が終わったらすべての処理が完了しました
という文字を出力して終了です。
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
という仮想環境を有効にする
conda activate selenium
- Pythonの実行ファイルがある階層までディレクトリ移動する。
cd ...
cd ...
cd ...
- Pythonファイルを実行
python scraping.py
入力項目に値を入力(パス入力はファイルをドラックアンドドロップ)
すべての処理が終わったら以下のような画面になります。
保存先のファイル保存されていたら成功です。
2022年5月分のデータをダウンロードした結果以下のように保存することができました。
最後に
見ていただいてありがとうございました。実際に実装してもらうとわかりますが10年分のデータを集めようとすると30分以上はかかってしまいます。time.sleep
がボトルネックになっている感はあります。すべて手探りで調整したものなので最適ではないと思われます。サイトのほうでも選択してから日付けが表示されるまでに時間かかっているので手作業でやるとストレスがかかります。一度実行したらあとは勝手にやってくれる自動化のほうがストレスフリーなので結果的に自動化してよかったです。