突发奇想,研究一下 ACG 歌词的用词特点。
获取歌词 
找到了一个共有 4572 首歌(2023-5-10)的歌单,https://music.163.com/#/playlist?id=66652611  ,尝试获取其中所有歌的歌曲 id。
首先尝试查找有没有合适的 API,失败,尝试从歌单页面入手。
可以看到,网易云网页查看别人创建的歌单只可以看到前 20 首歌曲。但是如果我们将歌单复制到自己的自建歌单,则可以显示 1000 首。理论上可以创建 5 个歌单,然后进行获取。
分析网页结构,可以发现每个歌曲都是一个 a 标签,里面的 href 就是我们要的内容。
尝试在控制台编写一个 JS 来获取所有个歌曲 id。
1 document .querySelectorAll ("div.f-cb span.txt > a" )
 
尝试使用这个选择器,发现能够成功获取所有 a 标签,接下来就是获取所有 a 标签的 href 属性。
1 2 let  result = "" document .querySelectorAll ("div.f-cb span.txt > a" ).forEach ((x )=> {result+=x.href +"\n" })
 
使用 .forEach 进行遍历,得到 result 后复制,则得到了一份歌曲列表。
接下来,使用 python 获取所有歌词。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import  requestsimport  jsonimport  reimport  timedef  get_lyric (song_id ):     url = "http://music.163.com/api/song/lyric?id={}+&lv=1&tv=-1"      r = requests.get(url.format (song_id))     jsonObj = json.loads(r.text)     lrc = jsonObj["lrc" ]["lyric" ]     result = ""      for  line in  lrc.split("\n" ):         if  '作词'  in  line or  '作曲'  in  line or  '編曲'  in  line or  '詞:'  in  line:             continue          result += line[line.find(']' )+1 :]+"\n"      return  result songs = open ("songlist.txt" ,"r" ).read().split("\n" ) unique_songs = list (set (songs))print (f"{len (songs)}  Songs, {len (unique_songs)}  Unique. ({100 *len (unique_songs)/len (songs):.2 f} %)" ) pat = r"https://music.163.com/song\?id=([\d]+)"  now = 34  tot = len (unique_songs)with  open ("dataNew.txt" ,"a" ,encoding="utf-8" ) as  f:     while  now != tot:         try :             song = unique_songs[now]                          print (f"\tnow #{now} : " ,re.search(pat,song).group(1 ))             lrc = get_lyric(re.search(pat,song).group(1 ))             f.write(lrc)         except  KeyboardInterrupt:             break          except :             print ("FAILED!!!!!!!!!!!!!!!!" )         now += 1 
 
分词 
搜索对日语进行分词,得到了 MeCab 库。
1 2 pip install mecab-python3 pip install unidic-lite
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import  MeCab text = """残酷な天使のように 少年よ 神話になれ 蒼い風がいま 胸のドアを叩いても 私だけをただ見つめて 微笑んでるあなた そっとふれるもの もとめることに夢中で 運命さえまだ知らない いたいけな瞳 """  mecab_tagger = MeCab.Tagger() x = mecab_tagger.parse(text)open ("out.txt" ,"w" ,encoding="utf-8" ).write(x)
 
得到的结果是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 残酷	ザンコク	ザンコク	残酷	名詞-普通名詞-形状詞可能			0 な	ナ	ダ	だ	助動詞	助動詞-ダ	連体形-一般	 天使	テンシ	テンシ	天使	名詞-普通名詞-一般			1 の	ノ	ノ	の	助詞-格助詞			 よう	ヨー	ヨウ	様	形状詞-助動詞語幹			1 に	ニ	ダ	だ	助動詞	助動詞-ダ	連用形-ニ	 少年	ショーネン	ショウネン	少年	名詞-普通名詞-一般			0 よ	ヨ	ヨ	よ	助詞-終助詞			 神話	シンワ	シンワ	神話	名詞-普通名詞-一般			0 に	ニ	ニ	に	助詞-格助詞			 なれ	ナレ	ナル	成る	動詞-非自立可能	下一段-ラ行	連用形-一般	2 蒼い	アオイ	アオイ	青い	形容詞-一般	形容詞	連体形-一般	2 風	カゼ	カゼ	風	名詞-普通名詞-一般			0 が	ガ	ガ	が	助詞-格助詞			 いま	イマ	イマ	今	名詞-普通名詞-副詞可能			1 胸	ムネ	ムネ	胸	名詞-普通名詞-一般			2 の	ノ	ノ	の	助詞-格助詞			 ドア	ドア	ドア	ドア-door	名詞-普通名詞-一般			1 を	オ	ヲ	を	助詞-格助詞			 叩い	タタイ	タタク	叩く	動詞-一般	五段-カ行	連用形-イ音便	2 て	テ	テ	て	助詞-接続助詞			 も	モ	モ	も	助詞-係助詞		 ...	 ... EOS
 
我们只保留实词(名词,形容词,动词),同时还原回原形。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def  process (src ):     result = ""      for  line in  src.split("\n" ):         if  line == "EOS" :             break          word, hin = line.split("\t" )[3 ],line.split("\t" )[4 ]         if  not  (hin.startswith("名詞" ) or  hin.startswith("動詞" ) or  hin.startswith("形容詞" )):             continue          if  word[0 ].isascii():              continue          result += word.split('-' )[0 ]           result += " "      return  result
 
特别的,我们删去了所有英语词汇以及外来语的英语部分。
这样函数返回的结果就可以直接用来绘制词云。
绘制词云 
使用 wordcloud 库。
 
通过指定字体,使其支持中文。
1 2 3 font_path = "C:\\WINDOWS\\FONTS\\MEIRYO.TTC"   wc = WordCloud(width=1920 ,height=1080 ,font_path=font_path,background_color="white" ).generate(result) wc.to_file("wc.png" )
 
完整代码 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import  MeCabfrom  wordcloud import  WordCloudfrom  collections import  Counterdef  process (src ):     result = ""      for  line in  src.split("\n" ):         if  line == "EOS" :             break          word, hin = line.split("\t" )[3 ],line.split("\t" )[4 ]         if  not  (hin.startswith("名詞" )):              continue          if  word[0 ].isascii():             continue          result += word.split('-' )[0 ]          result += " "      return  result mecab_tagger = MeCab.Tagger() parse_result = mecab_tagger.parse(open ("dataNew.txt" ,encoding="utf-8" ).read()) result = process(parse_result)open ("words.txt" ,"w" ,encoding="utf-8" ).write(result)print (len (result.split()),"words" ) counter = Counter(result.split())with  open ("frequency2.csv" ,"w" ,encoding="utf-8" ) as  f:         for  word,freq in  counter.most_common():         f.write(f"{word} ,{freq} \n" ) font_path = "C:\\WINDOWS\\FONTS\\MEIRYO.TTC"   wc = WordCloud(width=1920 ,height=1080 ,font_path=font_path,background_color="white" ).generate(result) wc.to_file("wc-noun.png" )
 
结论 
所有实词 
可以看到,出现最多的 5 个是日语中很常用的几个动词和表示否定的 ない。
词语 
出现次数 
 
 
為る 
5083 
 
居る 
4006 
 
無い 
3851 
 
行く 
3076 
 
成る 
2478 
 
 
其中 なる(為る、成る) 共出现了 5083+2478=7561 次,这是因为日语中 なる 承担了很多功能。(~になる、~Aくなる)。
いる(居る) 共出现了 4006 次,いる 除了表示有生命的物体存在,还可以作补助动词表示动作的进行、习惯等,也非常常用。
行く 可以表示去,还可以作补助动词表示动作的持续。
名词 
尝试查看歌曲中名词的词云。
最多的是 こと(事),猜测因为 こと 可以作为形式体言以及部分表达方式中的一部分,导致频率极高。
以下是其他出现频数超过 100 的名词(按出现频率排序)。
今、心、夢、世界、一、手、未来、時、空、明日、中、日、度、物、光、胸、侭、思い、涙、自分、声、今日、全て、人、為、二人、目、風、何、言葉、愛、前、星、道、本当、笑顔、一人、恋、場所、気持ち、夜、先、日々、皆、花、编曲、気、運命、側、希望、全部、瞳、一緒、歌、終わり、意味、色、勇気、闇、痛み、幸せ、力、二、月、嘘、回、雨、永遠、答え、願い、時間、悲しみ、筈、約束、記憶、见、奇跡、景色、後、街、音、顔、命、朝、孤独、瞬間、向こう、最後、鼓動、過去、昨日、共、影、毎日、思い出、果て、夏、傷、感情、真実、背中、下、駄目、自由、翼、始まり、絶対、絆、身、頃、奥、神、上、所、間、方、現実、不安、太陽、扉、理由、九、欠片、温もり、爱、大人、幾、体、物語、出会い、息、季節、遠く、年、是、雲、秘密、隣り、梦、最高、次、名前、゙、海、形、仆、旅、ハート、风、アニメ、虹、期待、歩、訳、魂、指、頬、强、宇宙、後悔、其々、迷い、輝き、存在、笑い、振り、限界、時代、羽根、言、理想、テーマ、魔法、人生、壁、青春、姿、夜明け、一瞬、数、祈り、桜、不思議、以上、一杯
可以发现,ACG 歌曲的用词确实有一些特点。