Jリーグの試合結果を分析する。〜totoBIGを当てるために〜

Bookmark this on Google Bookmarks
Pocket

BeautifulSoup

大したことはしません。Jリーグ公式サイトから試合結果をいただいて、コンピュータが扱いやすい形にしてあげるだけです。

スクレイピングには当然、BeautifulSoupを使います。

まずは公式サイトからどのようにスクレイピングしたら良いか探りましょう。

試合結果を公表しているページはここです。https://data.j-league.or.jp/SFMS01/

なにやら、色々と選択肢がありすぎて困りますが、いやはやこれは膨大なデータ量になりそうな予感

と思いきや、urlのパターンがわかれば簡単でした。

例えば、2016年のJ1の広島の全試合結果であれば、こうなります。

https://data.j-league.or.jp/SFMS01/search?competition_years=2016&competition_frame_ids=1&team_ids=10&home_away_select=0&tv_relay_station_name=

最後のテレビ何ちゃらは余計なので削除です。

  • competition_years=[年]
  • competiton_frame=[数字]
  • team_ids=[数字]
  • home_away_select=[数字]
  • これらが、検索のキーとなっているようです。

    jleague.py

    def game_data(year,competition,team_id,home_away):
        if year=='' or year is None:
            a=''
        else:
            a='competition_years=%s' % year
        if competition=='' or competition is None:
            b=''
        else:
            b='&competition_frame_ids=%s' % competition
        if team_id=='' or team_id is None:
            c=''
        else:
            c='&team_ids=%s' % team_id
        d='&home_away_select=%s' % home_away
        url = 'https://data.j-league.or.jp/SFMS01/search?'+a+b+c+d
        print(url)
        html = urllib.request.urlopen(url).read()
        soup = BeautifulSoup(html, "html5lib")
    

    soupを出すところまで書いてみました。
    とりあえず、game_data(year,competiton,team_id,home_away)で、検索結果のページを出すことができます。
    途中、空白やのNoneの条件を指定しているのは、チームを指定しない場合だとかで入力しない場合はごっそり消す必要があったからです。
    僕は初心者なので、ドンドンごり押してイキマス。

    データを綺麗にまとめる

    スープをゲットしたところで、テーブルの中の要素は使いにくい状態に置かれています。使いにくいというのは、コンピュータにとってであり、

    というわけで、soupの続きはこのようになります。

        game_data=[]
        search_table=soup.find('table',{"class":"search-table"})
        line=search_table.find_all('tr')
        for i in range(1,len(line)):
            year = line[i].find_all('td')[0].text
            month = re.split('\(|\)|/|(|)|月|火|水|木|金|土|日|祝|・',line[i].find_all('td')[3].text)[0]
            day = re.split('\(|\)|/|(|)|月|火|水|木|金|土|日|祝|・',line[i].find_all('td')[3].text)[1]
            kohour=re.split(':',line[i].find_all('td')[4].text)[0].replace('\n','').replace('\t','')
            komin=re.split(':',line[i].find_all('td')[4].text)[1].replace('\n','').replace('\t','')
            home=line[i].find_all('td')[5].text.replace('\n','').replace('\t','')
            away=line[i].find_all('td')[7].text.replace('\n','').replace('\t','')
            score_home=re.split('-',line[i].find_all('td')[6].text)[0].replace('\n','').replace('\t','')
            score_away=re.split('-',line[i].find_all('td')[6].text)[1].replace('\n','').replace('\t','')
            stadium=line[i].find_all('td')[8].text.replace('\n','').replace('\t','')
            game=(int(year),int(month),int(day),int(kohour),komin,home,away,score_home,score_away,stadium)
            game_data.append(game)
        return game_data
    

    search_tableというのは、もちろん検索結果ページでスクレイピングの対象となるテーブルです。
    次にlineは、そのテーブルの行になります。
    次のfor文 では、頭を除く全ての行に関して、試合結果のテキストを取得しています。
    例えば試合の日付は、○月○日(土)といったように、漢字やかっこが含まれているので、re.split() でまとめて分割しちゃいます。
    そのあとで、分割されたリストから月と日付にあたる部分を抜き出しています。

    所々replace() を使っているのは、\nや\tが発生するためです。なんでだろ?
    スコアは同様にハイフンを消しています。
    これで、綺麗にデータをまとめることができました。

    なぜスタジアムの名前も取得しているのかというと、後々にスタジアムの天気まで把握しようと考えているからです。

    結果

    game_data=game_data(2016,1,'',0)
    print(game_data)
    

    実行結果は、まぁすごい量の文字数になるので割愛しますが、次のようなフォーマットで出てきます。
    [(2016,02,27,16,04,’広島’,’川崎F’,0,1,Eスタ)]
    これでスタジアムのところをあとで気象庁から持ってきた当時の天気情報に差し替えたら完璧じゃないっすかね

    この得られたデータをどう使うかはあなた次第。

    例えば、今年の名古屋の勝ち点計算だって簡単にできちゃいます。

    def team_season(team,home,home_score,away,away_score,point):
        if team in home:
            print(home,home_score,away_score,away)
            if int(home_score)-int(away_score)<0:
                point+=0
                print('負け 勝ち点:%s'%str(point))
            elif int(home_score)-int(away_score)>0:
                point+=3
                print('勝ち 勝ち点:%s'%str(point))
            elif int(home_score)-int(away_score)==0:
                point+=1
                print('引分け 勝ち点:%s'%str(point))
            print('-'*40)
        elif team in away:
            print(away,away_score,home_score,home)
            if int(away_score)-int(home_score)<0:
                point+=0
                print('負け 勝ち点:%s'%str(point))
            elif int(away_score)-int(home_score)>0:
                point+=3
                print('勝ち 勝ち点:%s'%str(point))
            elif int(away_score)-int(home_score)==0:
                point+=1
                print('引分け 勝ち点:%s'%str(point))
            print('-'*40)
        else:
            pass
    
    def sample(team='広島',point=0):
        for i in range(len(game_data)):
            year =game_data[i][0]
            month =game_data[i][1]
            day =game_data[i][2]
            hour =game_data[i][3]
            minute =game_data[i][4]
            home =game_data[i][5]
            away =game_data[i][6]
            home_score =game_data[i][7]
            away_score =game_data[i][8]
            stadium =game_data[i][9]
            team_season(team,home,home_score,away,away_score,point)
    sample(team='名古屋')
    
    ----------------------------------------
    名古屋 1 1 磐田
    引分け 勝ち点:1
    ----------------------------------------
    名古屋 0 3 神戸
    負け 勝ち点:0
    ----------------------------------------
    名古屋 1 3 湘南
    負け 勝ち点:0
    ----------------------------------------  
    

    こんなふうに出てきます。あー腹たつ。
    というわけで、今はどのような条件で勝ちが多いかそれぞれのチームで検証しています。

    あとはスタジアムの天気情報ひっぱらなかん