#目次
1.記事の対象者
2.誰にどうに聞いたのか
3.使った技術
4.手順
5.結果
6.やってみての感想
7.おまけ
##1. 記事の対象者
人生とは何か知りたい人
##2. 誰にどうに聞いたのか
美空ひばりさん、山口百恵さん、中森明菜さんの歌詞をスクレイピングし、word2vecで学習したモデルを作りました。そのモデルを使い、歌詞の中から「人生」と類似度の高い単語を見つけ、昭和の歌姫から「人生」とは何か教えて頂きました。
##3. 使った技術
自然言語処理 ( janome、word2vec )
スクレイピング ( BeautifulSoup )
##4. 手順
####①データの抽出
こちらから、上位200曲の歌詞データをBeautifulSoupでスクレピングします。
・BeautifulSoupとは?
➡ 欲しいデータを抽出してくれるPythonの便利なライブラリ
・スクレイピングとは?
➡ 情報を抽出する行為や技術のこと (今回は歌詞データを抽出することです。)
# Webサイトの情報を取得するために、
# BeautifulSoupとrequestsモジュールも合わせてインポートします。
import requests
from bs4 import BeautifulSoup
# 歌ネットのURL、歌詞のURL、取得したい曲の数を定義しています。
base_url = "https://www.uta-net.com"
target_url = "歌詞のURL"
music_num = 200
# 歌詞のURLからrequestsを使って歌詞情報を取得し、r に代入します。
r = requests.get(target_url)
# BeautifulSoupで歌詞情報を解釈し、soupに代入します。
soup = BeautifulSoup(r.text, "html.parser")
# 曲一覧から各曲のURLを取り出して空の箱url_list = []に入れていきます。
url_list = []
for i in range(music_num):
href = soup.find_all("td", attrs={"class": "side td1"})[i].contents[0].get("href")
url_list.append(href)
# 曲ごとにRequestを送り、歌詞を抽出します。
kashi = ""
for i in range(music_num):
target_url = base_url + url_list[i]
r = requests.get(target_url)
soup = BeautifulSoup(r.text, "html.parser")
for string in soup.find_all("div", attrs={"id": "kashi_area"})[0].strings:
kashi += string
# 歌詞データを保存します。
with open('./kashi_txt/hibari_kashi_200', mode = 'w', encoding = 'utf-8') as fw:
fw.write(kashi)
これで200曲の歌詞データを抽出することが出来ました。
####②データの前処理
次に、正規表現で英数字や記号などの不要な文字列を削除し、日本語だけの歌詞に整えます。
例えば歌詞の中に「Wow!Yeah?」などありましたら、いらないので削除します。
# reモジュールをインポートし、sub関数を使って文字列を整えます。
import re
# 英数字の削除
kashi = re.sub("[a-xA-Z0-9_]","",kashi)
# 記号の削除
kashi = re.sub("[!-/:-@[-`{-~]","",kashi)
# 空白・改行の削除
kashi = re.sub(u'\n\n', '\n', kashi)
kashi = re.sub(u'\r', '', kashi)
####③形態素解析
歌詞データの辞書を作っていきます。
まずjanomeを使い、歌詞データに対して形態素解析を行います。
・janome
➡ 日本語の文章を形態素解析してくれる便利なライブラリです。( 詳しくはこちら )
・形態素解析とは?
➡ 形態素解析は、分かち書きした文章の「形態素」に「品詞付け」を行うことです。
形態素とは、それだけで意味のある最小の言葉のことです。
そして品詞付けとは、文章の中で、形態素の役割を明らかにすることです。
例えば、「私はお肉が大好きです。」という文章を形態素解析してみます。
# janomeのTokenizerモジュールをインポートします。
# こちらは基本的な使い方です。
from janome.tokenizer import Tokenizer
# Tokenizerインスタンスを生成し、形態素解析したい文章をtokenizeメソッドに渡します。
tokenizer = Tokenizer()
tokens = tokenizer.tokenize("私はお肉が大好きです。")
for token in tokens:
print(token)
結果は、下記のように文章が分かち書きによって形態素に分けられ、品詞付けにより"名詞", "動詞", "助詞" など、形態素の文章中の役割が明らかになりました。
< 結果 >
私 名詞,代名詞,一般,,,,私,ワタシ,ワタシ
は 助詞,係助詞,,,,,は,ハ,ワ
お 接頭詞,名詞接続,,,,,お,オ,オ
肉 名詞,一般,,,,,肉,ニク,ニク
が 助詞,格助詞,一般,,,,が,ガ,ガ
大好き 名詞,形容動詞語幹,,,,,大好き,ダイスキ,ダイスキ
です 助動詞,,,,特殊・デス,基本形,です,デス,デス
。 記号,句点,,,,*,。,。,。
今回の歌詞データの辞書は"名詞", "動詞", "形容詞", "形容動詞"の4つで作ります。
さらに、"動詞", "形容詞", "形容動詞"については、.base_formで、活用されているものを基本形に直します。
from janome.tokenizer import Tokenizer
def tokenize(text):
t = Tokenizer()
tokens = t.tokenize(text)
# 取り出した品詞が入るための空の箱word = []を作っておきます。
word = []
stop_word = create_stop_word()
for token in tokens:
part_of_speech = token.part_of_speech.split(",")[0]
if part_of_speech == "名詞":
if not token.surface in stop_word:
word.append(token.surface)
if part_of_speech == "動詞":
if not token.base_form in stop_word:
word.append(token.base_form)
if part_of_speech == "形容詞":
if not token.base_form in stop_word:
word.append(token.base_form)
if part_of_speech == "形容動詞":
if not token.base_form in stop_word:
word.append(token.base_form)
return word
####④ストップワードの作成
さらに、関係のなさそうな言葉をストップワードとして省きます。
ストップワードはこちらを参考にさせて頂きました。
また、追加したいストップワードがあれば自分で作ることも出来ます。
# 関係のなさそうな言葉を省くためのストップワード関数を定義しています。
def create_stop_word():
target_url = "ストップワードURL"
r =requests.get(target_url)
soup=BeautifulSoup(r.text, "html.parser")
stop_word=str(soup).split()
# 追加したいストップワードがあれば作ります。
my_stop_word=["自作のストップワード"]
stop_word.extend(my_stop_word)
return stop_word
これで歌詞データの辞書が完成しました。
####⑤word2vecで学習
辞書内の単語の関係性をword2vecで学習します。
・word2vecとは?
➡ 単語の特徴をベクトルで表現する技術のことです。これを使うことによって、似ている単語を見つけ たり、単語同士を足したり引いたり出来るようになります。( 詳しくはこちら )
model = word2vec.Word2Vec(sentence, size=200, min_count=4, window=4, iter=50)
size=200 : 単語ベクトルの次元数
min_count=4 : 出現頻度が数値未満であればその単語は無視
window=4 : 前後何個の単語を見てベクトルを作るかの数値
iter=50 : 何回学習するかの数値
####⑤類似度を計算する
.most_similarで、調べたい単語を入力すると、似ている単語を探してくれます。
topn=10で、1位~10位までを出力してくれます。
.most_similar(positive=["人生"], topn=10)
####①~⑤までをまとめると
from janome.tokenizer import Tokenizer
from gensim.models import word2vec
import re
import requests
from bs4 import BeautifulSoup
base_url = "https://www.uta-net.com"
target_url = "歌詞のURL"
music_num = 200
r = requests.get(target_url)
soup = BeautifulSoup(r.text, "html.parser")
url_list = []
for i in range(music_num):
href = soup.find_all("td", attrs={"class": "side td1"})[i].contents[0].get("href")
url_list.append(href)
kashi = ""
for i in range(music_num):
target_url = base_url + url_list[i]
r = requests.get(target_url)
soup = BeautifulSoup(r.text, "html.parser")
for string in soup.find_all("div", attrs={"id": "kashi_area"})[0].strings:
kashi += string
with open('./kashi_txt/hibari_kashi_200', mode = 'w', encoding = 'utf-8') as fw:
fw.write(kashi)
import re
kashi = re.sub("[a-xA-Z0-9_]","",kashi)
kashi = re.sub("[!-/:-@[-`{-~]","",kashi)
kashi = re.sub(u'\n\n', '\n', kashi)
kashi = re.sub(u'\r', '', kashi)
def tokenize(text):
t = Tokenizer()
tokens = t.tokenize(text)
word = []
stop_word = create_stop_word()
for token in tokens:
part_of_speech = token.part_of_speech.split(",")[0]
if part_of_speech == "名詞":
if not token.surface in stop_word:
word.append(token.surface)
if part_of_speech == "動詞":
if not token.base_form in stop_word:
word.append(token.base_form)
if part_of_speech == "形容詞":
if not token.base_form in stop_word:
word.append(token.base_form)
if part_of_speech == "形容動詞":
if not token.base_form in stop_word:
word.append(token.base_form)
return word
def create_stop_word():
target_url = "ストップワードURL"
r =requests.get(target_url)
soup=BeautifulSoup(r.text, "html.parser")
stop_word=str(soup).split()
return stop_word
sentence = [tokenize(kashi)]
model = word2vec.Word2Vec(sentence, size=200, min_count=4, window=4, iter=50)
print(model.wv.most_similar(positive=[u"人生"], topn=10))
これで人生と似ている単語上位10個までを調べることが出来るようになります。
##5. 結果
ついに、昭和の歌姫から「人生」とは何かご回答を頂くことが出来ました。( コードはこちら )
####美空ひばりさん
1位 死ぬ ( 0.9719272255897522 )
2位 うれしい ( 0.9682350158691406 )
3位 ない ( 0.9639120697975159 )
4位 いのち ( 0.9611829519271851 )
5位 僕 ( 0.9546191692352295 )
6位 あいつ ( 0.9545842409133911 )
7位 する ( 0.9526689052581787 )
8位 この世 ( 0.9476727843284607 )
9位 わかる ( 0.9469277858734131 )
10位 苦しい ( 0.9456108212471008 )
これを勝手に解釈すると、美空ひばりさんにとって人生とは
「死ぬものであり、なくなるなる命と分かるからこそ嬉しいもの」
ということでしょうか..
「死ぬ」や「いのち」という言葉がストレートで胸に刺さりますね。
ご自身の芯の強さのようなものを感じます。
###山口百恵さん
1位 終り ( 0.986318051815033 )
2位 短い ( 0.9854386448860168 )
3位 愛し合う ( 0.9821532964706421 )
4位 生まれる ( 0.9803616404533386 )
5位 思い過ごす ( 0.9801797270774841 )
6位 ただ ( 0.9794270992279053 )
7位 暗い ( 0.9787865877151489 )
8位 閉じる ( 0.9771473407745361 )
9位 あける ( 0.976866602897644 )
10位 失う ( 0.9767454266548157 )
これを勝手に解釈すると、山口百恵さんにとって人生とは
**「終わりがあり、短いものだけれども、愛し合うもの」**ということでしょうか..
「終わり」や「短い」という言葉から、なんだか儚さや潔さを感じますね。
美空ひばりさんとは違ったしなやかな強さのようなものも感じます。
###中森明菜さん
1位 ゲーム ( 0.9926275610923767 )
2位 合う ( 0.9812482595443726 )
3位 ドラマ ( 0.979516863822937 )
4位 つくる ( 0.9782220721244812 )
5位 辿る ( 0.973213255405426 )
6位 今度 ( 0.972602128982544 )
7位 なくす ( 0.9723961353302002 )
8位 時代 ( 0.9709115624427795 )
9位 こわれる ( 0.9708400368690491 )
10位 ふれる ( 0.9702985286712646 )
例にならって勝手に解釈させていただくと、中森明菜さんにとって人生とは
**「ゲームであり、ドラマを作るように出合うもの」**ということでしょうか..
これまた他のお二方と違ったテイストの人生観をお持ちのようですね。
時代の中で、なくしたり、こわれたりしてもゲームのように楽しめたらいいですよね。
##6. やってみての感想
単語の類似度を調べるだけでも、その歌手の「売り」のようなものが見えてきて面白かったです。
例えば100人くらいの歌手の曲を解析して、ポジティブ傾向な歌手、ネガティブ傾向な歌手、ニュートラルな歌手(?)のような分類を行ってみるのも面白そうだと思いました。
また、ポジティブ傾向な歌手の曲を好むのは、性格的にポジティブな人が多いのか、ネガティブ傾向な人はネガティブ傾向の曲を聴くのか、どうなのかという事も興味が湧いてきました。
##7. おまけ
昭和から令和時代になっても輝き続ける大スター、郷ひろみさんにも人生とは何なのか聞いてみました。
1位 楽しい ( 0.9172373414039612 )
2位 だらけ ( 0.8988165855407715 )
3位 罠 ( 0.89873868227005 )
4位 足りる ( 0.8761640191078186 )
5位 進める ( 0.8726701736450195 )
6位 思い出す ( 0.8624307513237 )
7位 アアアア ( 0.8605970740318298)
8位 向かう ( 0.8529828786849976)
9位 束縛 ( 0.8508580327033997 )
10位 勝負 ( 0.8425825238227844 )
またまた、例にならって勝手に解釈させていただくと郷ひろみさんにとって人生とは
**「罠だらけで、アアアア楽しい!」**ということでしょうか..?
ご自身のポジティブな生き様が歌詞にも表れていて素敵ですよね。