WordGrain/BarScan - ヒップホップ歌詞分析ツールの実装
2026-02-11
WordGrainというフォーマットとBarScanというCLIツールを作った これらはまだ公開できてないアプリケーションのサブプロジェクト、という位置づけではあるが、一旦ここまでで振り返ってみる
BarScanはPyPIにも公開した
きっかけ
ラッパーの語彙を集めたい
公開できていない制作中のアプリケーションでラッパーのペルソナでAIがしゃべる、というのをやりたくて、 ラッパーの語彙を集める必要がでた
Rap Geniusは時々みていたのでそこから収集できそうだし、テキストであるかぎり形態素解析などなどのテクニックで処理できるだろうと思ってPythonで作り始めた
実装
Genius API統合
Genius API統合では、lyricsgeniusライブラリをラップしたクラスを実装した
Genius APIへの過剰なアクセスを防ぐために、取得した歌詞はJSON形式でキャッシュし、TTL(デフォルト1週間)による自動期限管理とMD5ハッシュによるディレクトリ分散で効率的に保存している
NLTK自然言語処理
NLTK自然言語処理パイプラインでは、歌詞テキストに対してセクションヘッダー除去([Verse]、[Chorus]など)→正規化→トークナイズ→レンマタイズ→フィルタリングの順で前処理を行っている
言語は日本語文字の有無で自動検出して英語はNLTK、日本語はJanome形態素解析に切り替える
フィルタリングは非アルファベット除去/最小文字数/ストップワード除去/カスタムフィルターの4層構造 単語頻度カウント、TF-IDFによる単語の希少性定量化、品詞タグ付け、VADERによる感情分析(positive/neutral/negative)、ヒップホップスラング検出といった拡張NLP機能が入っている
日本語ストップワードの処理はstopwordsisoパッケージを利用しているが、少しむずかしい J-POPやヒップホップのリリックでは英単語が混在するため、日本語ストップワードに加えてNLTKの英語ストップワードも合成して、 トークン照合時は原形とlowercaseの両方をチェックすることで日英混合テキストに対応している
コンテキスト抽出機能では単語の前後2語を含むスニペット(SHORT)または曲名・アルバム情報を含む完全メタデータ(FULL)の2モードを作成した
複数出力形式
CLIは--formatオプションで4種類の出力形式(table/json/csv/wordgrain)に対応している tableはRichライブラリによるターミナルテーブル表示、jsonとcsvは汎用フォーマットでの単純な頻度データ出力
WordGrainフォーマット
なぜ作ったか
WordGrainフォーマットは当初つくる予定がなかったのだが、複数のツールやアプリケーションで使うのであれば、と思い立って作った
フォーマットなので実装はないが、ドキュメンテーションのサイトにvalidator/visualizerとplaygroundとして2つのWordGrainフォーマットのファイルを比較できるコンテンツを用意した
WordGrainのドキュメントサイトもあるのでそちらも見てみてほしい
構造
WordGrain形式(.wg.json)は独自に定義したJSON Schemaに準拠した語彙分析データの標準化フォーマットで、 メタデータ(アーティスト名、分析日時、コーパスサイズ、総単語数、生成ツールバージョン、言語コード)と単語エントリ(grains)の2層構造を持つ
各grainには生カウント、1万語あたりの正規化頻度に加え、拡張NLPフィールドとしてTF-IDFスコア(0.0〜1.0)、品詞タグ、感情カテゴリとVADERスコア、スラングフラグ、コンテキスト文字列をオプショナルで格納できる
BarScan経由で出力されたものは以下のようになる($schemaのURLは省略)
{
"$schema": "https://raw.githubusercontent.com/shimpeiws/word-grain/.../wordgrain.schema.json",
"meta": {
"source": "genius",
"artist": "JP THE WAVY",
"generated_at": "2026-02-11T08:58:34.052598Z",
"corpus_size": 10,
"total_words": 1848,
"generator": "barscan/0.2.1",
"language": "ja"
},
"grains": [
{
"word": "wavy",
"frequency": 31,
"frequency_normalized": 167.75,
"tfidf": 0.1715,
"pos": "adjective",
"sentiment": "neutral",
"sentiment_score": 0.0,
"is_slang": true,
"contexts": [
{ "line": "超Wavyでごめんね", "track": "Cho Wavy De Gomenne Remix" }
]
},
{ "word": "bit", "frequency": 28, "frequency_normalized": 151.52, "tfidf": 1.0, "pos": "noun", "is_slang": false },
{ "word": "見る", "frequency": 22, "frequency_normalized": 119.05, "tfidf": 0.1743, "pos": "noun", "is_slang": false }
]
}
振り返り
自然言語処理入門
個人的にはこれまで自然言語処理にはほとんど触れてこなかった、多少意識するのはElasticSearchを使うときだけだった
今回もリサーチ-設計-実装と全面的にClaude Codeのフルサポートで進めたが、 企画を元に一旦動くものを作ってもらってその説明をまたClaudeにしてもらう、というのを繰り返すと不慣れな自然言語処理の世界にも入門できた
NLTKがよく整備されていて、英語に限った範囲であればかなりここだけで処理できてしまう しかし、日本語となると途端に...ではある
今後の展望
BarScanは出力にはまだイマイチだと思う部分が残っているので、まだ改良の余地があるし、 WordGrainは使ってみたらもっと項目を増やしたくて、みたいにしてv2を作るくらい自分で使ってみたいと思っている
→ BarScan on PyPI
→ WordGrain Docs
→ GitHub: BarScan
→ GitHub: WordGrain