Help us understand the problem. What is going on with this article?

【SKY-HI】歌詞の特徴をWordCloudで可視化

More than 1 year has passed since last update.

はじめに

私は画像処理をメインに研究をしていますが、卒論を書き終え、息抜きがてら新しい世界を見てみたいと、最近自然言語処理をやっています。そんな中、これらの記事に出会いました。

米津玄師の歌詞をWordCloudで可視化してみた。
WordCloudで凛として時雨の歌詞の傾向を可視化する

なるほど。じゃあ、僕が推しているSKY-HIでもやってみようじゃないか、そう思ったのが今回の記事の動機です。SKY-HIの世界へようこそ。

(【FLYERSへ】結果に飛ぶと、JAPRISONが味わえます。)

SKY-HI

6人組スーパーパフォーマンスグループAAAのメンバーである日高光啓さんは、実はソロのラッパーSKY-HIとしても活躍しています。その活躍は日本にとどまらず、韓国のラッパーと曲を作ったり、世界ツアーを行なったりと、名実ともに日本を代表するラッパーの1人です。(もうちょっと日本のみなさんに知ってもらいたい!!!)
2019年2月現在、「SKY-HI TOUR 2019 "The JAPRISON"」開催中です!

やりたいこと

WordCloudは、テキスト中で出現頻度が高い単語を複数選んできて、その頻度を大きさに反映させた画像を作ることができるツールです。例えば、以下の画像はWordCloudによって作成されたものです。
image.png
【引用:WordClouds.com

これをSKY-HIの最新アルバム「JAPRISON」の歌詞に対して適用し、歌詞を可視化、そしてJAPRISONの曲たちの歌詞の世界観を新しい視点から味わおう、というのが今回の目的です。

なお、今回の実行環境は以下の通りです。

python 3.6.8
macOSX 10.13.6 (High Sierra)

準備

今回使うツールの準備をしていきます。まず、形態素解析エンジンとして公開されているMeCabと、MeCabの辞書に当たるMeCab-IPADICをインストールします。また、MeCabをPythonで使えるようにするツールとしてMeCab-Python3も入れておきます。
(余談ですが、Wikipedia先生によると、MeCabという名前はMeCab開発者が和布蕪好きだったことにより命名されたようです。超余談ですね。)

brew install mecab mecab-ipadic
pip install mecab-python3

この時点でMeCabは使えるようになりました。
ただ、今インストールした辞書だけでは、うまく行かないことがあります。そこで新語に対応した辞書MeCab-IPADIC-NEologdが提供されています。これは、Homebrewでは入れられないので、直接githubからクローンすることになります。

cd /usr/local/lib/mecab/dic
git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
./bin/install-mecab-ipadic-neologd -n

さて、MeCabの準備が整いました。実際にTerminalでmecabと叩くとMeCabが起動して、形態素解析ができます。

terminal
$ mecab
AAAの日高はラッパーです
AAA 名詞,固有名詞,組織,*,*,*,*
の 助詞,連体化,*,*,*,*,の,ノ,ノ
ラッパー    名詞,一般,*,*,*,*,*
です  助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
EOS

「AAA」が名詞(固有名詞)、組織名であり、「の」は助詞、「ラッパー」は一般名詞、「です」は助動詞であると判定されました。このように、文中の言語を、意味をもつ最小単位(形態素)の列に分割して、それぞれの形態素の品詞等を分類することを形態素解析と言います。

あとは、今回の主役WordCloudを入れます。

pip install wordcloud

以上で、ツールの準備は完了です。次は実際にWordCloudで遊びます。

Let's WordCloud!

今回使うものたちをimportしておきます。

import os
from os import getcwd
import re
import bs4
import time
import requests
import matplotlib
import pprint
from wordcloud import WordCloud, ImageColorGenerator
import MeCab
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import random

歌詞の取得

このセクションについては、こちらを参考にしました。ここでは、歌ネットに掲載されているSKY-HIの曲の歌詞を抽出します。

def load(url):
    res = requests.get(url)
    res.raise_for_status()

    return res.text

def pickup_tag(html, find_tag):
    soup = bs4.BeautifulSoup(str(html), 'html.parser')
    paragraphs = soup.find_all(find_tag)

    return paragraphs

def scraping_web_page(url):
    html = requests.get(url)
    soup = bs4.BeautifulSoup(html.content, 'html.parser')
    return soup

def parse(html):
    soup = bs4.BeautifulSoup(str(html), 'html.parser')
    # htmlタグの排除
    kashi_row = soup.getText()
    kashi_row = kashi_row.replace('\n', '')
    kashi_row = kashi_row.replace(' ', '')

    # 記号の排除
    kashi_row = re.sub(r'[ <>♪`‘’“”・…_!?!-/:-@[-`{-~]', '', kashi_row)
    # 注意書きの排除
    kashi = re.sub(r'注意:.+', '', kashi_row)

    return kashi


with open('SKY-HI.txt', 'a') as f:

    # アーティストページ(SKY-HI)のアドレス
    url = f'https://www.uta-net.com/artist/15174/'

    # 曲ページの先頭アドレス
    base_url = f'https://www.uta-net.com'

    # ページの取得
    html = load(url)

    # 曲ごとのurlを格納
    musics_url = []
    # 歌詞を格納
    kashis = ''

    """ 曲のurlを取得 """
    # td要素の取り出し
    for td in pickup_tag(html, 'td'):
        # a要素の取り出し
        for a in pickup_tag(td, 'a'):
            # href属性にsongを含むか
            if 'song' in a.get('href'): 
                # urlを配列に追加
                musics_url.append(base_url + a.get('href'))

    """ 曲名の取得 """  
    titletemp = []
    titles = []
    soup = scraping_web_page('https://www.uta-net.com/artist/15174/')titletemp.append(soup.find_all(href=re.compile('/song/\d+/$')))
    for i in range(len(titletemp[0])):
        temp = titletemp[0]
        titles.append(re.split('[><]', str(temp[i]))[2])

    """ 歌詞の取得 """
    for i, page in enumerate(musics_url):
        print('{}曲目:【{}】 \n from {}'.format(i + 1,titles[i], page))
        html = load(page)
        for div in pickup_tag(html, 'div'):
            # id検索がうまく行えなかった為、一度strにキャスト
            div = str(div)
            # 歌詞が格納されているdiv要素か
            if r'itemprop="text"' in div:
                # 不要なデータを取り除く
                kashi = parse(div)
                print(kashi, end = '\n\n')
                # 歌詞を1つにまとめる
                kashis += kashi + '\n'

                # 1秒待機
                time.sleep(1)
                break
    # 歌詞の書き込み
    f.write(kashis)

これを実行すると、曲とそれに対応する歌詞が表示されます。
ここでは、最初の2曲分だけお見せします。

出力
1曲目:【New Verse -Remix- feat. eill】 
 from https://www.uta-net.com/song/262891/
ゴールの旗はどこ俺の居場所はこここっちを向いてよこっちよ君の為の目を閉じてリラックス、オープンスピーカー越しの君、調子はどう素っ裸の心のはるか上空まで飛ぶのに必要ないアルコール夢から覚めた時半笑えないニュースに落ちた時や心無い言葉がまた刃を研いだどきな、どきな、鬼さんこちら望んでいた未来望まれた期待そこにある人生全て愛していたい転んだら手痛いミス待ちの奴でいっぱい怖さも請け負うさ全部持ってきな笑っちまいな腫らした瞼の向こうの世界もありがたいなまだ痛みに会えた笑っちまいなその感傷も逃げ腰な自分もありがたいなまた君に会えた愛しても泣いても何度でも大したことあっても笑ってやろうシミ、次のテイクでボーカル上げてくれドラムを響かせてくれ照明の落ちたブースがやけに落ち着くのは自分と会話するツールだからさアイドル崩れの野郎口だけ達者な世渡り上手だろうラップも見かけも凄い奴なんて山ほどいる誰かの目で見るそこに生きる俺はいつもヒールいや俺自身が本当はそう思ってるのか人以上の報酬、プレッシャーの分の成功でも足に鎖、両手には手錠なぁドクター教えてくれこんな俺は正常どうしても答えが無いその時マイクの向こうの君を呼び自分自身を灯しまた帰ってこれるこの通りさぁ一緒に作ってみようぜ昨日よりもマシなストーリー笑っちまいな嫌なほど長い死ぬまでの時間もありがたいなまだ夢を見れた笑っちまいなその愛情も逃げちゃった自分もありがたいなまた僕に会えたいつかどこか枯れた涙雨が止めば腫れた瞼夢見た自分とは程遠いでも足掻く姿こそ愛おしい弱さを憎まなくてもいいどれも全部自分だからほらまた僕に会えた弱音が出る口を塞いで気がつけば口の中で腐って何が辛いのかもわからなくなるその前にねぇいつでも僕を呼んで笑っちまいな嫌なほど長い死ぬまでの時間もありがたいなまだ夢を見れた笑っちまいなその愛情も逃げちゃった自分もありがたいなまた僕に会えた愛しても泣いても何度でも大したことあっても笑ってやろう

2曲目:【愛ブルーム】 
 from https://www.uta-net.com/song/150035/
アスファルトされた街中で君だけが柔らかくてバグだらけの回路抜け出して連れ去る僕の役目風を泳ぐスカートにヒラリ乗っかって火遊び段取りは無視してハンドリングお望みならもう少しハードに君を誘うエージェントを上げてクレッシェンド心配ならいらない最高の一つ上をプレゼント花が色を差す茂みに足を伸ばすリアルの向こうへ先立ち宝探しのファンタジーの始まり風向きはイキナリ波打つようにみだり要するに今すぐにイキたいのさワンチャンス誘わせて近づいてストップ夢を見させて華やいで輝いて瞬いてく咲き走れ愛ブルームノイズだらけのより雲の上を見に行こう鮮やかに揺さぶる蜃気楼君の危うげな揺れをリピート自由、不自由、縛られ過ぎる下界を笑うように君がスウィング振り落とされないようにしがみつく僕にウインク太陽がジェラシー見惚れたお月様のフェイバリットタブーを破る女神神様も打つ手無し花が色を増す枯らさない様に水をやるどこまでだって僕が相手しなやかなカラダで明日を描いて鮮やかな手品に抵抗すら出来ないともするとこの先も君に落ちるワンチャンス誘わせて近づいてストップ夢を見させて華やいで輝いて瞬いてく咲き走れ愛ブルーム要するに今君とイキたいのさワンチャンス誘わせて近づいてストップ夢を見させて華やいで輝いて瞬いてく咲き走れ愛ブルーム

これが全72曲分続きます。なおそれぞれの曲の歌詞を格納したものを、テキストファイルSKY-HI.txtとして保存しています。

さて、ここからいきなり形態素解析に移っても良いのですが、実は今回使用するMeCabは英語に対応していません。SKY-HIの歌詞には、よく英単語が登場するので、これに対応するためにユーザー辞書を作ることにします。

ユーザー辞書の作成

まず、適当なところにこれからユーザー辞書となるcsvファイルを作ります。
そこには、次のような形で単語を登録していきます。
表層形,左文脈ID,右文脈ID,コスト,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音,オプション
詳しくは公式ドキュメントをご覧いただきたいです。例えば、SKY-HIという単語を辞書に登録するには、次のように入力します。

UserDic.csv
SKY-HI,,,10,名詞,固有名詞,人名,名,*,*,スカイハイ,すかいはい,すかいはい,ユーザー登録

このように活用形のない、例えば名詞などはこれで登録できますが、活用形のある単語は自分で活用語展開をしなければなりません。他のソフトウェアの場合、原形を入力すれば自動的にその活用形も登録してくれる、動的活用展開をしてくれることもありますが、MeCabの場合はリソースや計算コストの観点から対応していません。ただ、今回のようにWordCloudで可視化したいだけの場合、品詞が何であるかはそこまで重要ではありません。したがって、全ての英単語を名詞として登録してしまえば、作業量的には大幅に削減できます。(本当ならば、活用形も含めて辞書に登録したいです。。。)

さて、全ての単語の登録が済んだら、辞書ファイル(UserDic.csvとします)をコンパイルします。

$ /usr/local/libexec/mecab/mecab-dict-index \
-d /usr/local/lib/mecab/dic/ipadic \
-u user.dic \
-f utf-8 \
-t utf-8 UserDic.csv

(ディレクトリ位置については、各々違いますが、私の例では上の通りでした。)
mecab-dict-indexで辞書のコンパイルができます。オプションについては、以下の通りです。
● -d DIR : システム辞書があるディレクトリ指定
● -u hogehoge : hogehogeというユーザー辞書ファイルを作成
● -f code : CSVファイルの文字コード
● -t code : バイナリ辞書の文字コード

コンパイルが終わったら、パスを通しましょう。

$ vi /usr/local/etc/mecabrc

と叩けば

出力
;
; Configuration file of MeCab
;
; $Id: mecabrc.in,v 1.3 2006/05/29 15:36:08 taku-ku Exp $;
;
dicdir =  /usr/local/lib/mecab/dic/ipadic
; userdic = /home/foo/bar/user.dic

; output-format-type = wakati
; input-buffer-size = 8192

; node-format = %m\n
; bos-format = %S\n
; eos-format = EOS\n

~                                                                               
~            

などと出てきます。このdicdirの次の行あたりに

userdic = /usr/local/lib/mecab/dic/ipadic/user.dic

を入れれば、パスが通ります。
これで、ユーザー辞書の登録は完了です。実際に単語が登録されたか、確認してみましょう。

mecab
$mecab
SKY-HIはラッパーです
SKY-HI  名詞,固有名詞,人名,名,*,*,スカイハイ,すかいはい,すかいはい,ユーザー登録
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
ラッパー    名詞,一般,*,*,*,*,*
です  助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
EOS

はい、ちゃんとできています。
お待たせしました。ここから、Pythonで形態素解析をおこないます。

MeCabで形態素解析

形態素解析のお時間です。
まず歌詞を保存したテキストファイルを読み込み、それをutf-8でdecodeします。テキストファイルは1行に1曲分の歌詞が保存されているので、1行ごとに分割して、配列listとします。

bindata = open("SKY-HI.txt", "rb").read()
kashi = bindata.decode("utf-8")
lines = kashi.split("\n")

ここでどの曲を解析するか、選びます。その準備として、listに保存した曲たちについて何番目に何の曲が入っているかを明示しておきます。

title_table = []
for i , title in enumerate(titles):
    title_table.append([i, title])

ここでtitle_tableを叩くと

出力
[[0, 'New Verse -Remix- feat. eill'],
 [1, '愛ブルーム'],
 [2, 'アイリスライト'],
 ...(中略)...
  [70, 'Role Playing Soldier'],
 [71, 'One Night Boogie feat. THE SUPER FLYERS']]

と番号と歌詞が出てきます。この番号を見てlist[59]等と指定すれば、その番号にしたがって曲を選択できます。今回はアルバムJAPRISONのリード曲「What a Wonderful World!!!」を解析に使用します。
Mecab.Taggerというクラスのインスタンスを生成し、m.parseを使用することで、歌詞を形態素に分割してくれます。なお、Taggerでは使う辞書を指定できます。MeCab-IPADIC-NEologdに加えて、先ほど作成したユーザー辞書userdicも使用しましょう。

m = MeCab.Tagger("-Ochasen -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd /usr/local/lib/mecab/dic/userdic")
listwww= m.parse(lines[59]).split("\n")
出力
['なんとも\tナントモ\tなんとも\t副詞-一般\t\t',
 '世知辛く\tセチガラク\t世知辛い\t形容詞-自立\t形容詞・アウオ段\t連用テ接続',
 '見\tミ\t見る\t動詞-自立\t一段\t未然形',
 'ざる\tザル\tぬ\t助動詞\t特殊・ヌ\t体言接続',
 '言わ\tイワ\t言う\t動詞-自立\t五段・ワ行促音便\t未然形',
 'ざる\tザル\tぬ\t助動詞\t特殊・ヌ\t体言接続',
 '聞か\tキカ\t聞く\t動詞-自立\t五段・カ行イ音便\t未然形',
 'ざる\tザル\tぬ\t助動詞\t特殊・ヌ\t体言接続',
 '今日\tキョウ\t今日\t名詞-副詞可能\t\t',
 'も\tモ\tも\t助詞-係助詞\t\t',
 'プラスチック\tプラスチック\tプラスチック\t名詞-一般\t\t',
 '製\tセイ\t製\t名詞-接尾-一般\t\t',
 'の\tノ\tの\t助詞-連体化\t\t',
 '日\tヒ\t日\t名詞-非自立-副詞可能\t\t',
 'が\tガ\tが\t助詞-格助詞-一般\t\t',
 '差す\tサス\t差す\t動詞-自立\t五段・サ行\t基本形',
  ...(以下略)

注意:他の記事を見るとm.parseではなくm.parseToNodeを使用している例が多いですが最新版のMeCabでは、これを用いて形態素解析を行なった後に使用できる.surface属性にバグがあるようです。今回はそれを避けるためにm.parseを使用して、そこから形態素と品詞を抜き出す方式をとりました。

歌詞の中で印象的になりやすい品詞といえば、「名詞、動詞、形容詞、副詞」でしょうか。この4品詞の形態素を抽出します。また、これはWordCloudで可視化してからわかることですが、この4品詞の中でも、「その形態素抜き出されても...」となるものが登場することがあります。それが画像にデカデカと描かれても困るので、それらが除外されるようにignoreとして格納し、除外します。

words = []
yestype = ["名詞","動詞","形容詞","副詞"]
ignore = ["てる","あっ","さん","よう","それ","しよう","かお","くれ","中こ","モン","かかっ","こい"]
listleng = len(listwww)
for i, word in enumerate(listwww):
    if i < listleng-2:
        typet = word.split('\t')[3]
        typet = typet.split("-")[0]
        if typet in yestype:
            temp = word.split("\t")[0]
            if not temp in ignore:
                words.append(temp)

ここまでで完成した形態素列wordsは次のようになっています。

出力
['なんとも',
 '世知辛く',
 '見',
 '言わ',
 '聞か',
 '今日',
 'プラスチック',
 '製',
 '日',
 '差す',
 ...(以下略)

先ほどのlistwwwと比べて「ざる」(助動詞)などが消えていることがわかります。
ここまでで、形態素解析が終わりました。あとはWordCloudで可視化するだけです!

WordCloudで可視化

WordCloudで可視化する際に使用するフォントを指定する必要があります。SKY-HIは日本語詞なので、日本語対応するフォント「mplus-1m-regular」を使ってみました。日本語なら何でもいいです。

text = ' '.join(words)
fpath = "/Library/Fonts/mplus-1m-regular.ttf"

いよいよ可視化します。
SKY-HI「JAPRISON」は、黒背景に赤文字というCDジャケットが印象的なので、今回描くWordCloudもそんな感じにします。文字色を作成する関数を作っておきます。色はHSLで指定します。HSLについてはこちら(配色を考えるのが面倒ならhsl()を使おう)がわかりやすかったです。簡単に言うと

  • H:hue(色相)、0°~360°の角度で色を指定。0°=360°が赤色
  • S:saturation(彩度)、0%~100%で指定。値が高いほど純色、低いほど灰色に近づく
  • L:lightness(輝度)、0%~100%で指定。50%が純色。高いほど白くなり、低いほど黒くなる。
def red_color_func(word, font_size, position, orientation, random_state=None,**kwargs):
    return "hsl(%d, %d%%, %d%%)" % (random.randint(0, 1), random.randint(90, 100) ,random.randint(45, 55))

ではWordCloudで可視化、pyplotで描画までいきます。
背景は黒色、フォントと色調はこれまでに指定した通り、画面サイズは900×500です。

wordcloud = WordCloud(background_color = "black",
                      font_path = fpath,
                      color_func = red_color_func,
                      width = 900,
                      height = 500,
                     ).generate(text)

plt.figure(figsize = [20,20])
plt.imshow(wordcloud, interpolation = 'bilinear')
plt.axis("off")
plt.show()

出力は...
wordcloud_WhataWonderfulWorld!!!.png
できた!

What a Wonderful World!!!のフックは「ハローグッバイ」から始まるのですが、やはりそれが最も目立ちますね。JAPRISONというアルバムは「精神的や文化的な意味での監獄からそこを抜け出すための鍵を探す」ということがテーマの1つになっている(と思ってます)が、それがよくわかる、まさにリード曲という感じの結果になりました。

結果

JAPRISONの収録曲14曲をWordCloudで可視化したものを一気にご覧ください。

1. What a Wonderful World!!!

wordcloud_WhataWonderfulWorld!!!.png

2. Shed Luster

wordcloud_ShedLuster_Eng.png

3. Role Playing Soldier

wordcloud_RolePlayingSoldier_Eng.png

4. 23:59

wordcloud_23:59_Eng.png

5. White Lily

wordcloud_WhiteLily_Eng.png

6. Blue Monday

wordcloud_BlueMonday_Eng.png

7. Doppelgänger

wordcloud_Doppelganger_Eng.png

8. Persona

wordcloud_Persona_Eng.png

9. Shed Luster pt.2

wordcloud_ShedLuster_pt2_Eng.png

10. New Verse

wordcloud_NewVerse_Eng.png

11. Marble (Rerec for JAPRISON)

wordcloud_Marble3_Eng.png

12. Name Tag

wordcloud_NameTag_Eng.png

※JAPRISON収録のNameTagは「Name Tag-Remix-feat.Ja Mezz&HUNGER」ですが、歌ネットにはRemix ver.の歌詞が収録されていなかったため、「Name Tag feat. SALU & Moment Joon」で代用しています。

13. Diver's High

wordcloud_DiversHigh_Eng.png

14. Snatchaway

wordcloud_Snatchaway_Eng.png

おわりに

この記事では、SKY-HIの曲を題材に、MeCabを用いた形態素解析の基礎、WordCloudの使い方を学びました。たまには、自分の研究分野以外の内容に触れるのも楽しいものですね。自分の分野に囚われることなく、広い世界に飛び出す一歩になりました。監獄よ!ハローグッバイ!

参考
  1. python3で裁判の判例データからWord Cloudを生成する
  2. 新し目の辞書を使ってMeCabをPythonから利用する
  3. 配色を考えるのが面倒ならhsl()を使おう
  4. PythonでWordCloudを作成してみました
  5. 米津玄師の歌詞をWordCloudで可視化してみた。
  6. WordCloudで凛として時雨の歌詞の傾向を可視化する
  7. mecabユーザ辞書を追加
  8. 形態素解析

おまけ

AAAの代表曲「恋音と雨空」でもやってみました。
wordcloud_koi.png
やっぱ、SKY-HIとAAAじゃ雰囲気全然違いますね。

以上です。お疲れ様でした。

Dason08
医療画像の自動診断/農作物の収量予測/時系列/スポーツデータ解析
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away