u++の備忘録

Jリーグの戦評、「1秒あまり」で自動作成 Jリーグ公式サイトからテキスト速報をスクレイピングして試合を要約する

はじめに

先日(2018年7月24日)公開された下記の記事を見て、自分でも似たものを作ってみたくなりました。

www.itmedia.co.jp

神戸新聞社が開発した「経過戦評ロボットくん」

www.kobe-np.co.jp

記事などから読み取れた情報を基に、こちらのプログラムのアルゴリズムを、以下に示します。

  1. 神戸新聞が運営する「兵庫の高校野球|神戸新聞NEXT」にて、リアルタイムに(手動で)一打席速報を掲載
  2. 試合終了を契機にプログラムが起動
  3. 一打席速報のデータを読み込む
  4. ひとつひとつの打席結果を分析し、事前に用意した「得点表」を基にそれぞれの得点シーンについて勝敗への影響度を算出
  5. 高い点数がついた得点シーンを複数組み合わせ、文章として違和感なくまとめる

事前に用意した「得点表」は、記者が執筆し神戸新聞に掲載されてきた試合の戦評を基に、機械学習で作成されたとのことです。

詳細は記事などに記載がなかったので私の推察になりますが、以下のような教師あり学習だと思います。

  1. 「一打席速報」の文章を単語単位に形態素解析して分かち書きし、Bag-of-words形式で特徴ベクトル Xにする
  2. それぞれの「一打席速報」の文章が「記者が執筆し神戸新聞に掲載されてきた試合の戦評」に採用されていれば目的変数 Yを1、されていなければ0にする
  3.  X Yのペアを訓練データとして、重回帰分析などで Y=1に寄与している Xの要素を特定する

若干想像しづらいと思うので、架空の具体例を用いて説明します。

とあるA高校とB高校の試合で得られた「一打席速報」
1回表 A高校 3番 XXX「ライトスタンドへのホームランが飛び出し、A高校が1点を先制」
3回表 A高校 9番 YYY 「二死ランナー無しで、サードへの内野フライ」
9回裏 B高校 4番 ZZZ 「レフトスタンドへの逆転サヨナラ満塁ホームラン」

A高校とB高校について、実際に掲載された戦評
1回表、A高校は3番XXXのライトスタンドへのソロホームランで先制。しかし9回裏にB高校は4番ZZZが起死回生の逆転サヨナラ満塁ホームランを放ち、4-1で勝利を収めた。

この場合、訓練データとなる X Yのペアは以下のようになります。ただし Xの数値はそれぞれ(ライト, スタンド,ホームラン, 先制, 二死, ランナー無し, サード, 内野フライ, レフト, 逆転, サヨナラ, 満塁)の登場回数です。この単語群は、一打席速報の文章から作成しています。

 X  Y
(1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0) 1
(0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) 0
(0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1) 1

この訓練データを基に何かしらのアルゴリズム教師あり学習させることで、どの単語が含まれている「一打席速報」が、実際に「記者が執筆し神戸新聞に掲載されてきた試合の戦評」に入っているかが分かります。ここから、どの「一打席速報」を基にして戦評を作成すれば良いかを類推できるという仕組みです。

(今回の例で言うと「スタンド」「ホームラン」などが含まれる「一打席速報」は、戦評に含まれるべきと判定されます)

Jリーグ版を作った

読み取った情報を基に、Jリーグ版を作成しました。

github.com

生成した戦評

先に結果をお見せすると、例えばとあるJリーグの試合(浦和レッズVS川崎フロンターレ, 2018年8月1日(水)19:03KO)について、次のような戦評を生成できました。

[2-0でホームの浦和が勝利。J1通算400勝を達成した]
浦和は前半7分、岩波からのロングボールに武藤が抜け出す。持ち上がった武藤は右サイドの敵陣深くからグラウンダーで折り返すと、興梠が右足でチョンソンリョンの頭上を越えるループシュートを放ち、ゴールに吸い込まれる。浦和は後半47分、李が自陣からドリブルで持ち上がる。ペナルティエリア左に進入すると、鈴木にタックルで倒されてPKを獲得。キッカーはファブリシオ。チョンソンリョンの逆を突き、ゴール左に沈め、リードを広げる。2-0でホームの浦和が勝利。J1通算400勝を達成した。

アルゴリズムの概要

アルゴリズムの概要を以下に示します。いろいろ検討したのですが、サッカーの場合は野球ほど1試合当たり点数が入らないので、「経過戦評ロボットくん」のように機械学習で事前に「得点表」を用意する必要がない気がしました。ガバガバですが、ゴールが入ったプレー全てを戦評に含めるルールベースの実装になっています。

  1. Jリーグ公式サイトからプレーのテキスト速報をスクレイピング
  2. ゴールが入ったプレーのテキスト速報を基に、文言を微調整して戦評を作成
テキスト速報のスクレイピング

以下のURLをプログラムにコマンドライン引数として渡すことで、サイトからテキスト速報をスクレイピングします。

www.jleague.jp

スクレイピングには、seleniumを利用しています。最初はbeautifulsoup4を使おうと思いましたが、上記のサイトはテキスト速報部分を別途動的に読み込む仕様になっていたのでブラウザ経由でhtmlを取得できるseleniumを採用しました。

ゴールが入ったプレーか否かの判定

ゴールが入ったプレーのテキスト速報には、下図のように「GOOOOOOOOOOAL!」という画像が挿入されていました。

f:id:upura:20180805153153p:plain

このことを活かし、画像タグが挿入されているか否かで判断をするロジックを組みました。

    # ゴール判定
    goal_list = driver.find_elements_by_class_name('spotRightTxt')    # クラス名'spotRightTxt'の全要素をリスト形式で取得
    goals = [1 if '<img class=' in goal.get_attribute("innerHTML") else 0 for goal in goal_list]    # 要素に画像タグがあれば1, なければ0のリスト内包表記
文言を微調整して戦評を作成

テキスト速報の末尾には句点がなかったり、PKの場合にはPKが発生した文章も戦評に含めたかったりと、今回の要件に沿うように戦評を作成しました。

おわりに

より高度な戦評を作成する場合、機械学習を用いた「得点表」の作成も選択肢に入ってくるかと思います。例えば分量の制限があり全得点については掲載できない場合や、重要な選手交代やファールについて取り上げる場合なども想定できます。こちらは訓練データを作成するのが大変だと思うので、もしやる気が出たらやってみようと思います。