日本語歌詞って単語で終わることが多くないですか?という話と検証

洋楽も聴くようになってから最近気になっていることがあるんですけど、日本語って歌詞が単語の連なりになっているケースが多い気がしているんですよね。洋楽だと、歌詞は文章になっていることが多いような気がしています。気のせいでしょうか?個人的に気になったので調べてみました。

ざっとこんな感じでコードを書きました。
日本語の歌詞は、名詞で終わっているかどうかで判定します。
英語の歌詞は、主語動詞があるかどうかで判定します。

import requests
import langid
from musixmatch import Musixmatch
import spacy
import MeCab

#SPOTIFY API
client_id = "<YOUR_client_id>"
client_secret = "<YOUR_client_secret>"
auth_url = "https://accounts.spotify.com/api/token"
auth_data = {
    "grant_type": "client_credentials",
    "client_id": client_id,
    "client_secret": client_secret
}
response = requests.post(auth_url, data=auth_data)
access_token = response.json()["access_token"]

#MUSIXMATCH API
musixmatch = Musixmatch("<YOUR_API>")

Analyzed = 0
English_sentence_count = 0
Japanese_sentence_count = 0
English_phrase_count = 0
Japanese_phrase_count = 0
Exception = 0

world_top_id = "37i9dQZEVXbLp5XoPON0wI"
japan_top_id = "37i9dQZEVXbKqiTGXuCOsB"

def is_japanese_noun(line):
    # MeCabのインスタンスを作成
    mecab = MeCab.Tagger()

    # 文章を形態素解析して結果を取得
    result = mecab.parse(line)

    # 最後の形態素を取得
    last_morpheme = result.strip().split('\n')[-2]

    # 形態素の各情報を取得
    last_morpheme_info = last_morpheme.split('\t')
    pos = last_morpheme_info[4].split(',')[0]  # 品詞

    # 最後の形態素が名詞であるか判定
    if '名詞' in pos:
        return True
    else:
        return False

def is_english_sentence(line):
    # 英語の文章として成立しているかを判定する関数
    # 主語と動詞が存在すればTrue、それ以外はFalseを返す
    nlp = spacy.load('en_core_web_sm')
    doc = nlp(line)
    for token in doc:
        if token.dep_ == 'nsubj' or token.pos_ == 'VERB':
            return True
    return False

def get_song_list (id):
    global Analyzed
    global English_sentence_count
    global Japanese_sentence_count
    global English_phrase_count
    global Japanese_phrase_count
    global Exception

    # プレイリストの各楽曲情報を取得する関数
    playlist_id = id
    playlist_url = f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks"
    headers = {
        "Authorization": "Bearer " + access_token
    }
    response = requests.get(playlist_url, headers=headers)
    data = response.json()

    for track in data["items"]:
        # 必要な情報を抽出して利用する
        track_name = track["track"]["name"]
        artist_name = track["track"]["artists"][0]["name"]
        print("\n********************************************")
        print(f"Track: {track_name}, Artist: {artist_name}")
        # 歌詞の取得
        get_lyrics(track_name,artist_name)

    # Reset
    Analyzed = 0
    English_sentence_count = 0
    Japanese_sentence_count = 0
    English_phrase_count = 0
    Japanese_phrase_count = 0
    Exception = 0

def get_lyrics(track_name, artist_name):
    global Exception
    global Analyzed
    # 歌詞を検索します
    print(f"Getting Lyrics: {track_name} by {artist_name}")
    Analyzed += 1
    song = musixmatch.matcher_lyrics_get(track_name, artist_name)
    try:
        lyrics = song['message']['body']['lyrics']['lyrics_body']
    except:
        Exception += 1
        print("Track was not found: " + track_name)
        print("********************************************\n")
        return

    # 商用利用が禁止されている場合、取得した歌詞に警告が含まれるのでそれらと改行を除外する
    lyrics = remove_empty_lines_and_text(lyrics,"******* This Lyrics is NOT for Commercial use *******")
    lyrics = remove_empty_lines_and_text(lyrics,"...")

    if lyrics:
        if  "en" in langid.classify(lyrics)[0] or "ja" in langid.classify(lyrics)[0]:
            analyze_lyrics(lyrics)
        else:
            Exception += 1
            print("It's not an English or a Japanese track.: " + track_name)
            print("********************************************\n")
    else:
        Exception += 1
        print("Lyrics not found for the track: " + track_name)
        print("********************************************\n")

def remove_empty_lines_and_text(text, target_text):
    lines = text.split('\n')                        # 文字列を改行で分割してリストに変換
    cleaned_lines = [line for line in lines if line and line != target_text]  # 空の行と目標の行を除外
    result = '\n'.join(cleaned_lines)                # 行を改行で連結して文字列に戻す
    return result

def analyze_lyrics(lyrics):
    global Analyzed
    global English_sentence_count
    global Japanese_sentence_count
    global English_phrase_count
    global Japanese_phrase_count
    global Exception

    # 現在の行の最初の文字が'または大文字のアルファベットであることをチェックします。
    # 次の行の最初の文字が小文字であることをチェックします。
    # もし条件が満たされた場合、現在の行と次の行を連結し、concatenated_linesリストに追加します。
    pre_concatenated_lines = lyrics.split('\n')
    concatenated_lines = []
    for i in range(len(pre_concatenated_lines) - 1):
        current_line = pre_concatenated_lines[i]
        next_line = pre_concatenated_lines[i+1]

        if ((current_line.startswith("'") and current_line[1].isupper()) or current_line[0].isupper()) and next_line[0].islower():
            concatenated_lines.append(current_line + next_line)
        else:
            concatenated_lines.append(current_line)

    for line in concatenated_lines:
        line = line.strip()  # 行の前後の空白文字を削除
        if line:
            if "ja" in langid.classify(line)[0]:
                # 日本語の場合は名詞で終わっているか判定
                if is_japanese_noun(line):
                    Japanese_phrase_count += 1
                else:
                    Japanese_sentence_count += 1
            else:
                # 英語の場合は文章として成立しているか判定
                if is_english_sentence(line):
                    English_sentence_count += 1
                else:
                    English_phrase_count += 1                    

    print("Finished Analyzing")
    print("Analyzed               : " + str(Analyzed))
    print("English_sentence_count : " + str(English_sentence_count))
    print("Japanese_sentence_count: " + str(Japanese_sentence_count))
    print("English_phrase_count   : " + str(English_phrase_count))
    print("Japanese_phrase_count  : " + str(Japanese_phrase_count))
    print("Exception              : " + str(Exception))
    print("********************************************\n")

get_song_list(world_top_id)
get_song_list(japan_top_id)

分析対象のプレイリストはアメリカのTop50と日本のTop50です。

結果としてはこんな感じでした(2023/05/23現在)。

#USA

#Analyzed               : 50
#English_sentence_count : 642   (89%)
#Japanese_sentence_count: 0
#English_phrase_count   : 73    (10%)
#Japanese_phrase_count  : 0
#Exception              : 9
#Japan

#Analyzed               : 50
#English_sentence_count : 9
#Japanese_sentence_count: 368   (81%)
#English_phrase_count   : 2
#Japanese_phrase_count  : 75    (16%)
#Exception              : 19

分析出来たのはアメリカは40/50件くらい、日本は30/50件くらいで、それぞれ単語の歌詞の割合は10%と16%でした。

あれ??????あんまり差がありませんね。
なんででしょう???????分析件数少なかった???????
ってことで考えてみたんですけど、ジャンルも絞って考えた方がいいですね。というのも、僕が今までかなり偏った邦楽を聴いてきたからです。
体感ではthe cabsとか君島大空とかは単語の割合が多い気がしてます。
僕たちに明日はないとか、扉の夏とか。
今はとりあえずジャンルのせいにしてますけど、純粋に僕の勘違い説もありますからね。ちゃんとまともに歌詞を聴いてないことがばれますね。
もし勘違いじゃないとすれば、日本の詩が影響してそうですね。体言止めで余韻を出すというのもありますし。それは英語の表現技法にもあるんでしょうか?と思って、調べてみたら単語の連なりで書かれた詩もあるみたいです。

I think that I shall never see
A poem lovely as a tree.
A tree whose hungry mouth is prest
Against the earth’s sweet flowing breast;
A tree that looks at God all day,
And lifts her leafy arms to pray;
A tree that may in Summer wear
A nest of robins in her hair;
Upon whose bosom snow has lain;
Who intimately lives with rain.
Poems are made by fools like me,
But only God can make a tree.

Trees – Joyce Kilmer

こういうのは英語の歌詞に適用しづらいんでしょうか。
やっぱり語順が違うってことも考えられるんですかね。
語順が同じ韓国語だったらどうなるのかな。
あと、リズムを気にしてそうなので、Hiphopとかだったらまた違った結果が出てたかも。
というか、歌詞って歌詩じゃないんですね。なんでだろう。

徹夜でコード書いたのに、思った通りの結果が出なくてしょんぼりです。
悲しい。原因は色々考えられるので、そう遠くない時期にまたリベンジしようかな。では。

いいなと思ったら応援しよう!