LoginSignup
3
1

More than 3 years have passed since last update.

スポーツ記事から選手と技名を抽出してみた

Last updated at Posted at 2020-02-23

Motive

【Qiita x COTOHA APIプレゼント企画】関連の投稿です。

COTOHA API で照応解析してもギブミーチョコレート出来ていない問題とは別のAPIを使ってみました。

今回は
固有表現抽出(/nlp/v1/ne)
APIです。

MeCabだったら固有名詞を学習をして辞書に登録しないと人物名が抽出できなかった気がします。あと、KNPは精度は良さそうなのですがパッケージ自体重いです。:scream:
また、形態素解析すると機械学習された分配器が優秀なのか品詞までは正確に出力されるのですが、文章の中で多く出て来る名詞を分類できていたかと言うとそうでもない気がします。
COTOHAではAPIだけで名詞の細かい分類をしてくれます。

どこまで固有名詞を抽出できるかを簡易的にトライするために、スポーツ記事から人物名・技名を出力してみました。

Environment

Dataset

東京スポーツ
選定基準としては住んでいる地域ではこのスポーツ新聞が発売されていないという高尚な理由です。:camera_with_flash:

Method

前述の通り、
COTOHA API 固有抽出
https://api.ce-cotoha.com/contents/reference/apireference.html#parsing_io_part
を使っています。

選手(人物)はx["class"] == "PSN" and x["extended_class"] == ""、技名はx["class"] == "ART" and x["extended_class"] in [
"Doctrine_Method_Other"]

で抽出しています。Doctrine_Method_Other は(主義方式名_その他)という意味です。

名称 説明
ORG 組織名
PSN 人名
LOC 場所
ART 固有物名
DAT 日付表現
TIM 時刻表現
NUM 数値表現
MNY 金額表現
PCT 割合表現
OTH その他

Development

Script

スクリプトコード (クリックするとコードが表示されます。)
import argparse
import requests
from bs4 import BeautifulSoup
import json

#--- この4つパラメータはPortalで取得 ---
PUBLISH_URL = "--- get your parameter ---"
CLIENT_ID = "--- get your parameter ---" 
CLIENT_SECRET = "--- get your parameter ---" 
BASE_URL = "--- get your parameter ---"


class COTOHA:

    def __init__(self):
        self._token = self._getAccessToken()

    def _getAccessToken(self):
        header = {"Content-Type": "application/json"}
        contents = {
            "grantType": "client_credentials",
            "clientId": CLIENT_ID,
            "clientSecret": CLIENT_SECRET
        }
        raw_res = requests.post(PUBLISH_URL, headers=header, json=contents)
        response = raw_res.json()
        return response["access_token"]

    def compose(self, sentence):
        header = {
            "Authorization": "Bearer {}".format(self._token),
            "Content-Type": "application/json"
        }
        contents = {
            "sentence": sentence
        }
        raw_res = requests.post(
            BASE_URL +
            "nlp/v1/parse",
            headers=header,
            json=contents)
        response = raw_res.json()
        return response

    def properNoun(self, sentence):
        header = {
            "Authorization": "Bearer {}".format(self._token),
            "Content-Type": "application/json"
        }
        contents = {
            "sentence": sentence
        }
        raw_res = requests.post(
            BASE_URL +
            "nlp/v1/ne",
            headers=header,
            json=contents)
        response = raw_res.json()
        return response

    def keyword(self, sentence):
        header = {
            "Authorization": "Bearer {}".format(self._token),
            "Content-Type": "application/json"
        }
        contents = {
            "document": sentence
        }
        raw_res = requests.post(
            BASE_URL +
            "nlp/v1/keyword",
            headers=header,
            json=contents)
        response = raw_res.json()
        return response

    def coreference(self, sentence):
        header = {
            "Authorization": "Bearer {}".format(self._token),
            "Content-Type": "application/json"
        }
        contents = {
            "document": sentence
        }
        raw_res = requests.post(
            BASE_URL +
            "nlp/v1/coreference",
            headers=header,
            json=contents)
        response = raw_res.json()
        return response


def extract_norn_list(_apiobj, contents, condition):
    dst = []
    for p in contents:
        items = _apiobj.properNoun(p.text)["result"]
        _raw = list(filter(condition, items))

        # print(_raw)
        # 略称は除く
        for _p in _raw:
            name = _p["form"]
            _exist = False
            for pname in dst:
                if name in pname:
                    _exist = True
            if not _exist:
                dst.append(name)
    return dst


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--url")
    args = parser.parse_args()

    #APIオブジェクトを作成
    cotoha = COTOHA()

    #URLから記事を取得(東京スポーツ仕様)
    res = requests.get(args.url)
    soup = BeautifulSoup(res.text, 'html.parser')
    title_text = soup.find('title').get_text()
    contents = soup.find('div', {"class": "detail-content"}).find_all("p")

    #抽出条件
    def is_person(x): return x["class"] == "PSN" and x["extended_class"] == ""
    def is_attack(x): return x["class"] == "ART" and x["extended_class"] in [
        "Doctrine_Method_Other"]

    #選手を出力
    print(extract_norn_list(cotoha, contents, is_person))
    #技名を出力
    print(extract_norn_list(cotoha, contents, is_attack))

if __name__ == "__main__":
    main()

Command

python main.py --url https://www.tokyo-sports.co.jp/prores/ddt/1754700/

Consequence

2つの記事を使って実行します。

【新日1・5東京ドーム】みのる US王座防衛のモクスリー襲撃「誰にケンカ売ってんだ!」

dataset

新日本プロレスの年間最大興行「レッスルキングダム14」(5日、東京ドーム)で行われたIWGP・USヘビー級王座戦は、王者ジョン・モクスリー(34)がIWGPタッグ王者のジュース・ロビンソン(30)の挑戦を退け、初防衛に成功した。
 前夜(4日)の東京ドーム大会では、モクスリーがランス・アーチャー(32)から王座を奪回。ジュースはデビッド・フィンレー(26)とのコンビでタッグ王座を獲得した。その翌日に新王者同士の決戦となったが、モクスリーは昨年6月にジュースから同王座を奪っており、前夜のリング上で決着をつけることを宣言していた。
 序盤はジュースが快調に先制したものの、モクスリーは場外でイスを持ち出して背中に一撃。さらにはジュースの額にかみついた。WWE時代に“狂犬”として暴れ回った荒くれ者が、強引にペースを奪い返した。
 ジュースは豪快なハイアングルパワーボムで反撃したが、王者は足4の字固めから鉄柱を使った4の字と意表を突いた攻撃を連発した。挑戦者は雪崩式ブレーンバスターからジャックハマーにジャーマン。モクスリーのデスライダー(ダブルアーム式DDT)をかわして、ラリアートでぶち抜いた。
だが、王者は乱打戦から強烈なランニングニーを一閃。ジュースのパルプフリクションを切り返して、DDTから必殺のデスライダーを炸裂させて12分48秒、3カウントを奪った。
 試合後には入場テーマ曲が流れ、いきなり鈴木みのる(51)が登場。昨年12月8日の広島大会でモスクリーからデスライダーを見舞われており、険しい表情で怒りを隠せない。花道でジャージーを脱いで戦闘態勢に入ると、リング上で王者とエルボーを打ち合った。ド迫力のみのるは裸絞めからゴッチ式パイルドライバーでモクスリーをKOした。
 みのるはマイクを握ると「誰にケンカ売ってんだ、このヤロー! オレはプロレスラーの鈴木みのるだ。こいつのケンカ、オレが買ってやる!」と宣戦布告。US王座を巡る“狂犬”対“性悪男”の抗争勃発で、危険な香りが漂ってきた。
 みのるの話「誰にケンカ売ってんだ、おい。俺は、お前が俺の前に来るのを待ってたんだよ。ジョン・モクスリー…いや、ジョン・ボーイ、心してかかってこい。ぶち殺す」
 ジュースの話「すべてはここで終わりだ。ジョン・モクスリーは今日も俺より強かった。また超えられなかった。今日のことを考えたのは昨日の試合が終わってから。それまでまったく今日の試合のことなんて考えていなかった」

output

['ジョン・モクスリー', 'ランス・アーチャー', 'デビッド・フィンレー', '鈴木みのる', 'ジョン・ボーイ']
['足4の字固め', '雪崩式', 'ジャックハマー', 'ラリアート', '裸絞め']

【新日1・4東京ドーム】内藤 逆転でIC王座奪回も「目的はこのベルトじゃない」

dataset

新日本プロレスの年間最大興行「レッスルキングダム14」(4日、東京ドーム)で行われたIWGPインターコンチネンタル(IC)選手権は、内藤哲也(37)が王者のジェイ・ホワイト(27)を撃破。王座を奪回するとともに、5日東京ドーム大会でIWGPヘビー級王者(オカダ・カズチカVS飯伏幸太の勝者)とのダブルタイトルマッチに駒を進めた。
昨年9月の神戸大会でジェイに敗れ、昨年2度目となるIC王座からの陥落。東京スポーツ新聞社制定「プロレス大賞」ではノミネート「0」の屈辱も味わった。しかし、大観衆は“制御不能男”の復活を待ち望んでいる。序盤から大・内藤コールで背中を押すと、ジェイには容赦ないブーイングを浴びせた。
 内藤は場外でエプロンを使ったネックブリーカーを放って先制。ところが、ジェイのセコンドの外道が場外から内藤の足を引っ張りペースを乱す。王者は内藤の左ヒザに狙いを絞って攻め込んだ。内藤はコーナーからの飛びつきフランケンシュタイナーで反撃。相手に顔につばを吐きかけてから、串刺しの低空ドロップキックだ。
これでペースを握るかとみられたが、ジェイのDDTを食らって悶絶し、再び左ヒザを攻められた。場外にはバックドロップで投げ捨てられ、劣勢は変わらない。さらに裏足4の字固めでヒザを締め上げられた。
 大ピンチの内藤は顔をゆがませながらロープブレーク。何とか脱出すると、逆襲の浴びせ蹴りだ。さらにスパインバスター、回転式DDT、雪崩式フランケンシュタイナー、グロリアの猛攻。レフェリーがアクシデントでダウンする隙に外道が乱入したが、急所打ちで撃退した。
 勝負をかけた内藤は、コリエンド式デスティーノを連発。ジェイの必殺ブレードランナー(変型顔面砕き)を完全に防ぎ切ると、最後は渾身のデスティーノで3カウントを奪った。
 33分54秒の大激闘に逆転勝利。昨年1月からIWGPとIC、2冠奪取の野望を掲げてきた“制御不能男”は、完全復活へ一世一代の大舞台に挑む。
【内藤の話】「今回の2連戦の目的はこのベルトを取ることじゃないから。お客様は『内藤おめでとう』って声かけてくれてうれしいよ。でも、トランキーロ。今回の目的ではないから、そこは。さあて、明日の対戦相手はどっちかな。俺の予定はオカダ。理想も、オカダ。さあ、どうかな」
【ジェイ・ホワイトの話】「アイツ(内藤)はどこに行った…。俺は残念ながらみんなが作り上げたストーリーの脇役の一人だったということだったんだな。みんな、ジェイ・ホワイトが負けるのを望んでいたはずだ。お前らが好きな内藤が勝ったんだぞ。なぜ笑わないんだ。新しい俺のデスティーノ…運命が明日から始まる」

output

['内藤哲也', 'ジェイ・ホワイト', 'オカダ・カズチカ', '飯伏幸太', 'デスティーノ...運命']
['ネックブリーカー', 'バックドロップ', '足4の字固め', 'スパインバスター']

Consideration

  • 選手の名称は「デスティーノ...運命」以外主要メンバーは抽出ができている。一般的な人物名は問題なく分類できてそうです。
  • 技名ですが、残念ながらCOTOHA APIの分類には出てこないです。何回かAPIからの出力で一番抽出できてそうな組み合わせだったのがclass:ART, extended_class:Doctrine_Method_Otherだったので出力してみたのですが、「ハイアングルパワーボム」「コリエンド式デスティーノ」あたりが対象外になってしまいます。2番目の条件としてclass:ART, extended_class:Productも追加すると技名以外も抽出されるので100%は厳しかったです:tired_face:
  • スポーツ記事ではなく専門書であれば効力を発揮できるかもしれません。APIに下記のtypeのパラメータを付与することができるからです。(for Enterpriseユーザのみ、、、なので有料であれば利用可能です。)
param name
IT コンピュータ・情報・通信
automobile 自動車
chemistry 化学・石油工業
company 企業
construction 土木建築
economy 経済・法令
energy 電力・エネルギー
institution 機関・団体
machinery 機械
medical 医学
metal 非鉄・金属

PostScript

人物名抽出の精度はいいと言ったのですが、なぜか最近引退した「獣神サンダー・ライガー」が正しく抽出されなかったです。「ART:固有物名」の分類になっていました。:japanese_ogre:
これはタレント名鑑のスタッフに出動応援した方が良さそうでしょうか:thinking:
:sushi:としてです。

3
1
0

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
1