論理の流刑地

地獄の底を、爆笑しながら闊歩する

【分析準備】Jリーグの会見コメントからコーパスをつくる by rvest/RSelenium/RMeCab

Motivation

最近、マッシモの会見が面白くなってきたともっぱらの評判である。
記者への警戒心がやや解けてきたのか、わりとざっくばらんに色々と話してくれるようになっている。


前任者である風間監督も、なかなか言葉選びに独特のセンスやこだわりを感じさせる監督して定評があった。
そこで、「言葉」という視角から、マッシモさんと風間さんの特徴比較をできたら良いな、と思う。

しかし、その二人のデータを単純に比べるだけではいまいちJリーグの監督全体の言説空間における位置づけがわかりにくいし、なにより面白くない。
したがって、2019-2020のJ1の監督全員の会見コメントをとってきたうえで、LDAなどにかけていきたい。

<参考URL>

実装(データ取得)

具体的には、以下のような工程を踏んで処理することとなる。

  1. 取得対象のURLの取得
  2. URLから原テキストの取得
  3. RMeCab::docDFにかけやすいように、監督×試合ごとの単位でテキストファイルに書き出す

①:取得対象のURLの取得

J1の日程・結果検索のところで、カテゴリを「J1」かつ対象月を1~12月すべてにして、得られたURLから、「監督コメント」のリンクを取得していく。
あとは適当にHadley神のつくりたもうた、SelectorGadgetをつかいつつ得られたCSSセレクタによりアクセスしていく。

一応関数化する(年度やJ1 or J2を指定できるように)

getMatchDay_All <- function( cat ="j1",  year = 2020){
  md_url <- paste0("https://www.jleague.jp/match/search/?category%5B%5D=" ,cat,"&year=",year,"&month=01&month%5B%5D=02&month%5B%5D=03&month%5B%5D=04&month%5B%5D=05&month%5B%5D=06&month%5B%5D=07&month%5B%5D=08&month%5B%5D=09&month%5B%5D=10&month%5B%5D=11&month%5B%5D=12&section=")
  
  md_page <- read_html(md_url)#読み込み
  a_nodes <- html_nodes( md_page , css=".matchTable li:nth-child(3) a")
  
  href_arr <- sapply( a_nodes , function( a_node){
    return( html_attr(a_node , name="href"))
  }) #
  return( href_arr)
} #function 

#実行
href_j1m <- getMatchDay_All(cat = "j1", year = 2020)

#結果
 head(href_j1m)
# [1] "/match/j1/2020/022101/coach/" "/match/j1/2020/022201/coach/"
# [3] "/match/j1/2020/022202/coach/" "/match/j1/2020/022203/coach/"
# [5] "/match/j1/2020/022204/coach/" "/match/j1/2020/022301/coach/"

②:監督コメントの取得

こちらは動的生成されるコンテンツ*1であるので、rvestは力及ばずでRSeleniumの出番となる。

◆対象ノードの情報取得
まずは、cssセレクタを利用し、基礎的な情報をとってくる(list形式、1個目Home , 2個目Away)

get_ComeContents <- function(clnt,url){
  clnt$navigate( url)
  Sys.sleep(0.4)
  
  m_doc <- (clnt$getPageSource())[[1]]
  m_page <- read_html(m_doc)
  come_info <- html_nodes( m_page , ".commentArea .commentBox")
  return(come_info)
} #get_Mng

#実行
come_NV <- get_ComeContents(clnt , "https://www.jleague.jp/match/j1/2020/022201/coach/") #第一節 仙台vs名古屋戦


◆ノードからの情報の構造化
さて、最終的な目的はコーパスをつくることなので、それがしやすいようにデータ化する。
監督会見のコメントのテキストには、記者からの質問も含まれるので、それを除く*2
また、あとで整理がしやすいようにクラブ名と監督名も取得する

comeToData <- function(c_node){
  #内部関数  
  gsub_arr <- function(chr , frm_str, to_str){
      for( i in seq_along( frm_str)){
        chr <- gsub( frm_str[i], to_str[i],chr)
      }
      return(chr)
    } #gsub
    
  h4_node <-  read_html(as.character(html_node( c_node, "h4")))
  p_node <-  read_html(as.character(html_node( c_node, "p")))
  club_name <- html_text(html_node( h4_node , "span"))
  comment_txt_arr <-  sapply(html_nodes(p_node,xpath="//p/text()"), function(x){
     return( html_text(x))
   }) ##
  comment_txt <- paste0( comment_txt_arr, collapse="\n")
  manager_name <- trimws( gsub_arr(  html_text( html_node( p_node , css="span")) , c("\\]","\\[", "監督") , rep("",3)))
   
  return( list(Club= club_name , Manager =manager_name, Comment = comment_txt))
} #func
##実行
xtN <- comeToData( c_node = come_NV[[2]])
txtN$Club
#[1] "名古屋グランパス"
txtN$Manager
#[1] "マッシモ フィッカデンティ"
txtN$Comment
#[1] "\nわれわれのやりたいサッカー、つまり相手の陣地...

あとはファイルに書き込むだけある。
ただし、後述するようにdocDF()はファイル名を列名とするので、監督名、クラブ名、日付あたりの分類に使いやすい情報をファイル名にいれておくのがよい。

#関数、第一引数はget_ComeContents()の戻り値
comeToFile <- function( come_info , url ,dir ="C:/Users/XXX/Documents/01_data/Manager_txt/"){
  ## 日付をとってくる ##
  gsub_arr <- function(chr , frm_str, to_str){
    for( i in seq_along( frm_str)){
      chr <- gsub( frm_str[i], to_str[i],chr)
    }
    return(chr)
  } #gsub_arr
  date_v <-  substr( gsub_arr(url,c(".*/2020/","/coach/") ,rep("",2) )  , 1,4)
  c_long <- c('湘南ベルマーレ','浦和レッズ','ベガルタ仙台','名古屋グランパス','柏レイソル','北海道コンサドーレ札幌','川崎フロンターレ','サガン鳥栖','セレッソ大阪','大分トリニータ','清水エスパルス','FC東京','横浜F・マリノス','ガンバ大阪','サンフレッチェ広島','鹿島アントラーズ','ヴィッセル神戸','横浜FC')
  c_alph <- c("shon","uraw","send", "nago", "kasw","sapp","ka-f","tosu","c-os","oita","shim","fctk","y-fm","g-os","hiro","kasm","kobe","y-fc")
  club_df <- data.frame( Name = c_long, ALPH = c_alph)
    lapply(list( comeToData(come_info[[1]]) , comeToData(come_info[[2]])) ,function(club_info){
    mng_name <- gsub(" ", "",club_info$Manager) #監督名
    club_name <- as.character((dplyr::left_join( data.frame( Name = club_info$Club) ,  club_df , by ="Name"))[["ALPH"]] )
    fname <- paste0(c(date_v, club_name ,mng_name,".txt"), collapse="_")
    full_path <- paste0(dir , fname) #フルパスを指定
    come_txt <- gsub("\n" ,"" , club_info$Comment) #改行だけ消しとく
    write(come_txt , file=full_path)
  }) #lapply  
}#func

#実行する
comeToFile(come_NV , "https://www.jleague.jp/match/j1/2020/022201/coach/")

実行すると、以下のような感じでtxtファイルができあがっている。

あとは、先ほど取得したURL一覧から適当にループで回していけばよい。

実装(取得したtextからのコーパス化)

テキストを監督ごとに結合する

20/10/14時点までの204試合(J1)の監督会見のデータを、上の処理で取得できたわけである。
代行*3を除外すると、19名(神戸がフィンク、三浦の2名)の監督がいるので、監督ごとに結合を行う。

daikou_idx <- grep("コーチ" , mng_names)
kantoku_names <- mng_names[setdiff( 1:length(mng_names) , daikou_idx)]

##--- 関数:テキストファイル結合用 ---##
txtFileMerge <- function( fpath , new_path){
  file_tbl_l <-sapply( fpath, function(fp){
    rt <- read.table(fp)
    rt_chr <-paste0(lapply( rt[1,] ,  as.character),collapse="")
    return( rt_chr)
  })# lapply
  merge_txt <- paste0( file_tbl_l, sep="\n")
  write( merge_txt ,file=new_path)
}# f_name

old_fp_l <- list()
new_fp_l <- list()
for( i in seq_along( kantoku_names)){
  mng <- kantoku_names[i] #監督名
  mng_idx <- grep( mng, list.files(mng_dir))
  tgt_files <- list.files(mng_dir)[ mng_idx]
  tgt_fp <- paste0(mng_dir , "00_merged/",  mng, "_all.txt")  
  txtFileMerge( fpath = paste0(mng_dir, tgt_files), tgt_fp)
  old_fp_l[[i]] <-   paste0(mng_dir, tgt_files)
  new_fp_l[[i]] <-  tgt_fp
} #for

結合できた。
監督名ごとに結合された会見のテキスト

RMeCab::docDF()でコーパスをつくる

さて、監督ごとの結合ファイルもつくることができたのでコーパスをつくる
RMeCabのdocDFで読み込んでいく

library(RMeCab)
mng_j1_df20 <- RMeCab::docDF(target = paste0(mng_dir, "00_merged"),pos=c("名詞","形容詞","動詞","副詞"), type=1)

# > head( mng_j1_df20[,1:7],3)
# TERM POS1     POS2 アンジェポステコグルー_all.txt ザーゴ_all.txt
# 1    , 名詞 サ変接続                              0              0
# 2    0 名詞       数                              2              2
# 3   00 名詞       数                              0              0
# トルステンフィンク_all.txt ネルシーニョ_all.txt
# 1                          0                    0
# 2                          2                    0
# 3                          1                    0

うまく読み込めている。

次回予告

これで準備は完了した、次回記事ではこのコーパスをもとにして、参考URL①を参考にトピックモデルを適用していく。


Spontania feat. AZU - 同じ空みつめてるあなたに

Enjoy!!

*1:最近はこういうサイトが多いので、むしろrvestでゴリゴリいけるFootball LABのほうがめずらしいのかも

*2:厳密に考えると、自分から話す内容と質問への応答では意味合いはかわってくるが、ここでそれは区別しない

*3:四方田@札幌、ビベス@神戸