python初学者がボートレースの結果とレーサー情報を取得して連結する

記事
IT・テクノロジー
前回、やっとの思いで一年分のボートレースの結果を取得した中川です。

閲覧してくれた方、いいねを押してくれた方本当にありがとうございました。

今回は、まず前回取得したボートレースの結果(2232レース分)がpandasのデータフレームのdfという名前で格納されているので結果を可視化したいと思います。

前回のブログはこちらです!

そして、作業に取り掛かる前に前回取得したデータをcsvファイルに出力しておきます。


df.to_csv("任意のファイル名.csv",encoding='utf_8_sig')


encoding は文字化けを防ぐために指定しています。

そして、前回取得したデータを棒グラフで表示していきたいと思います。

matplotlib と、リストのデータを要素ごとにまとめてくれるモジュール collections をインポートしておきます。


import numpy as np
import pandas as pd
import requests
import re
import collections
import matplotlib.pyplot as plt
from bs4 import BeautifulSoup


次にcollection メソッドをつかって同じ要素(ここでは順位の1~6位を)の出現回数を数え、それを変数に代入します。


first = collections.Counter(df['一着'])


そして、まとめられた要素の中の個別の要素を取ってきてくれるくれるメソッド keys() と各要素が何個ずつ存在しているか教えてくれるメソッド values()を使います。

試しに一度、values() と keys()で得られた値を表示してみると。


print(first.keys())
print(first.values())

#出力
#dict_keys([2, 1, 4, 3, 6, 5])
#dict_values([326, 1355, 182, 232, 46, 91])


と、表示されます。

2は326、1は1355と順番に対応しており、これを棒グラフで表示すると、


plt.bar(list(first.keys()), list(first.values()))

スクリーンショット (3).png


無事、表示されました!

ですが、このデータだけを使って機械学習をして予測してもたぶん、一着には1番が来るよ!としか言わないやつができます。

なんとか、選手情報などのデータもこの中に取り込まなければいけません。

とりあえず選手情報の中から全国成績を取得しデータに格納します。

選手の出走表を取得できるURLは"ボートレースのサイト名/owpc/pc/race/racelist?rno=1&jcd=ボートレース場識別番号&hd=年日の8桁"です。

ここから、全国成績を取得していきたいと思います。

スクリーンショット (4).png

マークされている場所が取得したい情報になります。

マークされている箇所のHTMLは<"tb" class = 'is-lineH2'...>となっていますので、

soup.find_all("td",class_ = 'is-lineH2')

で取得して確認してみると、縦の列のひとまとまりごとにリストに格納されているようでした。ここでは[F0 , L0 , 0.18],[4.60,30.00,40.00]・・・というようになっています。

なので、


score_origin = soup.find_all("td",class_ = 'is-lineH2')
score_origin[0]


とすると、F0 , L0 , 0.18部分がまとまって表示されます。

個別に数字を収集するためには、.textメソッドを使います。

.textをつかうとリストの中に一文字一文字が分割された状態で帰ってきます。

なので、score_origin[0].text[0]とすると F という一文字が返ってきます。

この性質を利用して、全国成績を取得するために


score_origin[1].text[0:4]


と記載すれば 4.60 という1番艇の全国成績を取得できます!

そうして、これら六艇の情報を取得したとしても、その中から一番大きな数字のみを抽出する必要があります。

そこで調べて出てきたのが max([リストの要素]) というものでした!

以上を踏まえて、ボートレースの出走表から一番全国成績がいい艇番を取得したいと思います。


それから、二日経過致しました、、

時間がかかってしまった理由としましては

max([one,two,three,four,five,six]) one~sixまでは各艇番の選手全国成績が入っています。

そして、2020年の最初のボートレース情報の全国成績を取得するには


race_num = 0
for _ in range(12):
    race_num += 1
    url = 'ボートレースのサイト名/owpc/pc/race/racelist?rno={race_num}&jcd=12&hd=20200102'.format(race_num=race_num)
    res = requests.get(url)
    soup = BeautifulSoup(res.text , "html.parser")
    score_origin = soup.find_all("td",class_ = 'is-lineH2')
    one = score_origin[1].text[0:4]
    two = score_origin[6].text[0:4]
    three = score_origin[11].text[0:4]
    four = score_origin[16].text[0:4]
    five = score_origin[21].text[0:4]
    six = score_origin[26].text[0:4]
    maximum = max([one,two,three,four,five,six])
    if (one in maximum):
            print("1")
    elif (two in maximum):
            print("2")
    elif (three in maximum):
            print("3")
    elif (four in maximum):
            print("4")
    elif (five in maximum):
            print("5")
    elif (six in maximum):
            print("6")
    else:
            print("エラーだよ")
print("終わり")


こうすればかなりごり押しですが12レース分の全国成績1位の艇番を取得でき、2,2,4,2,6,3・・・と表示されるのですが、たった一日の情報を取得するために10秒ほどの時間を要してしまいます。

正直言ってすごくコスパが悪いです。

そしてこれをfor文で一年分のデータを取得しようとするとこれの200倍ほどある情報に加え、順位結果も出力して、不成立の場合の条件分岐などしていればとんでもない時間を要してしまい現実的ではないのではないかと思いました。

しかしながら、もっと効率的に動かせるプログラミングが思いつきませんでした、、

たぶんこれ以上考えても思いつかないのでとりあえずはこれで1か月分のレース結果と、成績が一番の艇番を取得したいと思います。

まず、結果を挿入するためのリスト score_top = [] を宣言し、ここに数値を append していきます。

そして、完成されたリストを新たに定義した kekka_score という辞書型に格納すれば完了となります。

今回はわかりやすいように全国成績が一番良い艇番を辞書に格納し表示することに集中したプログラムにしました。


month = np.arange(1,13,1)
day = np.arange(1,32,1)


score_top = []
for m in month:
    for d in day:
        m_2 = f'{m:02}'
        d_2 = f'{d:02}'
        url="ボートレースのサイト名/owpc/pc/race/resultlist?jcd=12&hd=2020{m_2}{d_2}".format(m_2 = m_2, d_2 = d_2)
        res = requests.get(url)
        soup = BeautifulSoup(res.text , "html.parser")
        if len(soup.find_all("span", class_='numberSet1_number')) < 60:
            print("{m_2}{d_2}, 処理をスキップします。".format(m_2 = m_2, d_2 = d_2))
            continue
        print("{m_2}{d_2}, データが存在します。".format(m_2 = m_2, d_2 = d_2))



まずここまでは前回と同じやり方で欠損がなければ、下部の処理を実行していくという条件分岐を行っています。

太字の 13 と 32 とすることで、1年分のデータを取得することができるのですが、実行するのに1時間くらいかかってしまいます。

本当に待ち時間がたまらなく長かったです。。

そしてこれに続けて、

        race_num = 0
        for _ in range(12):
            race_num += 1
            url = 'ボートレースのサイト名/owpc/pc/race/racelist?rno={race_num}&jcd=12&hd=2020{m_2}{d_2}'.format(race_num = race_num , m_2 = m_2, d_2 = d_2)
            res = requests.get(url)
            soup = BeautifulSoup(res.text , "html.parser")
            score_origin = soup.find_all("td",class_ = 'is-lineH2')
            one = score_origin[1].text[0:4]
            two = score_origin[6].text[0:4]
            three = score_origin[11].text[0:4]
            four = score_origin[16].text[0:4]
            five = score_origin[21].text[0:4]
            six = score_origin[26].text[0:4]
            maximum = max([one,two,three,four,five,six])
            if (one in maximum):
                    print(str(race_num) + ". 1")
                    score_top.append(1)
            elif (two in maximum):
                    print(str(race_num) + ". 2")
                    score_top.append(2)
            elif (three in maximum):
                    print(str(race_num) + ". 3")
                    score_top.append(3)
            elif (four in maximum):
                    print(str(race_num) + ". 4")
                    score_top.append(4)
            elif (five in maximum):
                    print(str(race_num) + ". 5")
                    score_top.append(5)
            elif (six in maximum):
                    print(str(race_num) + ". 6")
                    score_top.append(6)
            else:
                    print("エラーだよ")
kekka_score = {"全国成績":score_top}
df_score = pd.DataFrame(kekka_score)
print(df_score)


そして、ややこしかったのが太字のところで欠損値があるかどうか調べる時にスクレイピングを行うURLと全国成績を取得する時のURLが異なるので別に指定しなければならず、

.format メソッドを使って各値に変数を逐一代入しています。

今回は無事、

全国成績
0  2
1  2
2  4
3  2
4  6
5  3
・・・

という情報が取得され無事にデータに格納することができました。

これらを、前回取得した df に格納されたデータと連結させて一つのデータにしたいです。

色々調べると、どうやら連結の仕方によって異なった連結方法を使用しなければならないみたいでした。

今回の場合は取得したデータをそのまま横方向に連結させたいので

pd.concat([df, df_score], axis = 1)

として、これをprintすると

    一着 二着 三着 全国成績
0  2  4  5  2
1  1  4  3  2
2  4  5  2  4
3  1  2  3  2
4  1  5  2  6
5  3  2  1  3
6  1  3  2  1
7  1  4  3  2
8  2  6  3  6
9  1  3  5  1
10  1  5  6  5
11  1  4  3  2
・・・
・・・
・・・
のように表示され、無事順位と全国成績が一番の艇番の情報が連結されました。

これらの情報を駆使して、相関関係を導こうと思ったのですが、一着の数字と全国成績は同じになる確率は高くても比例しているわけではないので何か別のアプローチが必要だと分かりました。

せっかく取得した情報、なので、二着、三着の順位までしっかりと考慮した包括的な機械学習を次回では検討していきたいと思います。

まだまだ至らない点が多く、分かりづらい点がたくさんあったと思いますが、ご覧いただけて本当に感謝です。

ありがとうございました。

では、また!

サービス数40万件のスキルマーケット、あなたにぴったりのサービスを探す