这次我们来看一个带着娱乐属性的技术素材:TREASURE 成员崔玹硕、金道荣、朴炡禹相关的歌词内容,标题里的“D to the E, to the L-I-C-I-O-U-S”其实就是 Delicious 的字母拼写彩蛋。如果你只把它当粉丝安利看,那就错过了一个非常适合练手的 Python 文本分析场景。这篇文章会把这类歌词文本当成数据源,完整走一遍歌词获取、清洗、分词、词频统计、词云可视化、情感分析和批量数据集构建,全程不需要 GPU,纯 CPU 就能跑。
先给结论:这类歌词文本分析项目,核心不是显卡和显存,而是数据管线和特征可视化。整个方案不依赖任何付费接口,不涉及爬虫绕过,所有代码都是通用模板,需要根据你实际拿到的数据源做字段调整。适合正在学 Python 数据分析的读者、对 K-pop 歌词内容好奇的粉丝,以及想给歌曲文本分析加一个可视化展示的开发者。
这篇文章不会给你一个现成的“一键生成粉丝报告”软件,但会给你一套可复用的技术流程:把一首歌的歌词变成词频表,再把词频表变成词云;把歌词拆成行,逐行做情感打分;把多首歌曲的歌词汇总成 CSV,方便后续做成员对比或专辑对比。前三个场景特别适合第一次接触文本挖掘的人。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | 歌词文本挖掘与数据可视化方案 |
| 数据源 | TREASURE 相关歌曲歌词文本,需自行获取 |
| 核心功能 | 文本清洗、英文分词、词频统计、词云可视化、情感分析、批量 CSV 数据集构建 |
| 运行环境 | Python 3.9+,普通笔记本即可 |
| 是否需要 GPU | 不需要 |
| 是否支持批量任务 | 支持,可遍历多首歌曲/多个成员歌词后统一输出 CSV |
| API 能力 | 取决于歌词数据源是否提供公开接口;分析脚本本身可封装为函数或简易 API |
| 输出形式 | 词云图片、情感分布图、CSV 表格、控制台统计信息 |
| 适合场景 | 粉丝向内容分析、文本挖掘入门、数据可视化练习、歌词风格对比 |
这个表格把“能不能用、门槛高不高”讲清楚了。下面从分析和工程角度拆开讲。
2. 适用场景与使用边界
2.1 适合谁用
第一类是 Python 数据分析初学者。歌词文本短、结构相对整齐、不需要复杂模型就能出效果,比直接上手大语言模型友好得多。你可以把清洗、分词、词频统计这套流程拆成函数,逐步理解每一步在做什么。
第二类是对 K-pop 歌词内容感兴趣的人。如果你想对比不同成员参与的部分在词汇、情感倾向上有没有差异,歌词文本是一个可量化的切入口。虽然不能替代对舞台表现和音色的主观感受,但至少能把“这首歌出现了哪些高频词”变成一张图。
第三类是业余开发者。把这套分析脚本整理成命令行工具或简单的 Web 展示页,用 FastAPI 包一层接口,就能给粉丝群提供一个“输入歌名,返回歌词词云”的小工具。
2.2 不适合什么场景
不适合严肃的音乐产业研究。歌词文本无法反映旋律、编曲、人声表现,也不能代表作品质量。若想做艺人影响力分析,还需要音源数据、播放量、社交媒体讨论量等更多维度,单靠歌词文本说明不了大问题。
也不适合把歌词全文作为“自有数据”去分发。歌词受版权保护,直接爬取、整理并二次发布全文有法律风险。本文所有代码都假设你已经通过合法途径拿到歌词,并且只用于个人学习和非商业研究。不要在此基础上做“歌词大全下载”这类产品。
2.3 需要留意的边界
歌词文本中可能包含艺人姓名、作品名、专辑名等受版权保护的素材。在博客中展示词云、词频统计和情感分布图,属于对文本的分析结果,这类衍生内容相对安全;但直接把歌词原文成段粘贴到公开文章中,就要谨慎。涉及 TikTok、综艺片段、饭拍视频等衍生内容时,同样需要确认授权范围。
3. 环境准备与前置条件
3.1 硬件和操作系统
这个分析流程对硬件没有任何硬性要求。CPU 足够跑完所有步骤,内存方面,处理几十首歌词文本也只需要几百 MB 级别。Windows 10/11、macOS、Ubuntu 都能运行,依赖安装方式略有差异,但代码本身跨平台。
3.2 Python 版本和依赖包
建议使用 Python 3.9 及以上版本。需要安装以下库:
pip install requests pandas nltk vaderSentiment wordcloud matplotlib如果网络条件一般,可以拆开安装,避免某个包装不上影响整体:
pip install requests pip install pandas pip install nltk pip install vaderSentiment pip install wordcloud pip install matplotlibvaderSentiment是专门做英文情感分析的轻量库,适合歌词这种短文本;wordcloud用来生成词云;pandas负责最后的表格输出。nltk主要用来处理停用词和分词,也可以只用re加集合实现。
3.3 数据目录结构
建议在项目目录下建好输入和输出目录:
lyrics-analysis/ ├── lyrics/ # 存放歌词 txt 文件 ├── outputs/ # 存放图片和 CSV ├── main.py # 主脚本 └── requirements.txt # 依赖清单这样一个项目一个目录,后续做批量任务时不会把数据和代码混在一起。
4. 歌词数据获取与准备
4.1 获取方式对比
| 方式 | 优点 | 缺点 | 建议 |
|---|---|---|---|
| 公开歌词 API | 数据规整,可批量 | 很多需要注册 Key,且 ToS 限制多 | 先看服务条款 |
| 手动保存 txt | 最稳妥,无接口风险 | 数量多时效率低 | 单曲分析推荐 |
| 从歌词网站复制 | 获取快 | 版权风险高,反爬复杂 | 不推荐用于批量 |
| 官方专辑文案 | 授权相对清晰 | 不一定有完整歌词 | 可作为补充 |
从工程角度讲,除非确定某个 API 允许个人学习使用,否则最稳妥的方式是手动保存少量歌词文件。这样你可以完全控制数据源,不会被接口字段变化影响。
4.2 通用请求示例
如果你确认使用的歌词数据源提供公开 API,可以写一个通用请求模板。注意:下面的 URL 和参数只是框架示例,不是某个真实歌词服务的调用方式,实际使用时需要替换成目标服务文档里给出的地址和请求头。
import requests # 通用歌词 API 请求示例 # 实际 URL、请求头、参数需要根据目标歌词服务文档调整 url = "https://example.com/api/lyrics" params = { "artist": "TREASURE", "song": "Delicious" } headers = { "User-Agent": "Mozilla/5.0 (study-project)" } resp = requests.get(url, params=params, headers=headers, timeout=30) if resp.status_code == 200: print(resp.text[:500]) else: print("请求失败:", resp.status_code)这个模板能帮你快速判断数据源是否可用。如果返回的是 JSON,需要提取歌词字段;如果返回的是 HTML 页面,建议换一种方式,解析 HTML 的维护成本通常比手动保存高很多。
4.3 手动保存歌词文件
如果你选择手动保存,就把歌词逐行复制到一个 txt 文件里,文件命名为delicious.txt,存到lyrics目录下。读取时统一用 UTF-8 编码:
from pathlib import Path lyrics_text = Path("lyrics/delicious.txt").read_text(encoding="utf-8") print(lyrics_text[:200])歌词文件里通常包含歌名、艺人名等额外信息。正式分析前,可以只保留包含歌词内容的部分,或者后续在清洗阶段把这些噪声去掉。
5. 文本清洗、分词与词频分析
5.1 清洗函数设计
歌词文本一般比较短,但里面会有标点、换行、括号、英文大小写等问题。清洗函数的目标是把文本变成干净的英文单词列表。这里给出一版通用实现:
import re def clean_lyrics_to_words(text: str) -> list[str]: text = text.lower() # 只保留英文字母和空白字符,去掉标点、数字和符号 text = re.sub(r"[^a-z\s]", " ", text) # 按空白切分 words = text.split() return words如果歌词里包含韩文或中文,这套正则就把它过滤掉了。对英文歌词来说问题不大;如果需要保留韩文,需要额外写 Unicode 范围过滤,本文不展开。
5.2 停用词处理
英文歌词里高频的 the、a、and、to 并不一定反映内容主题,需要去掉。维护一个停用词集合:
stopwords = { "the", "a", "an", "and", "or", "of", "to", "in", "on", "for", "it", "is", "are", "was", "were", "be", "been", "being", "i", "you", "me", "my", "your", "we", "us", "our", "they", "them", "their", "this", "that", "these", "those", "do", "does", "did" }单曲分析时,还可以根据实际结果动态补充停用词。比如有的歌反复出现 “yeah”“oh”“baby” 这类语气词,如果它们对分析没有价值,就加进集合。
5.3 词频统计
分词后直接用collections.Counter统计:
from collections import Counter words = clean_lyrics_to_words(lyrics_text) filtered_words = [w for w in words if w not in stopwords and len(w) > 1] counter = Counter(filtered_words) print(counter.most_common(30))输出效果类似:
[("delicious", 12), ("know", 8), ("taste", 6), ("love", 5), ...]这里的数字是示例,不是真实歌词统计结果。实际运行时以你拿到的歌词文本为准。
5.4 为什么要先小样本验证
第一次跑通时不要一上来就处理几十首歌。选一首歌的歌词,确认清洗后的单词列表长度、停用词是否覆盖到位、Top 词是否符合直觉。比如一首表达甜蜜主题的歌,高频词里如果出现大量 “sad”“cry”,就要检查清洗或停用词逻辑是否有问题。
建议把“单曲 → 词频表”作为最小可运行闭环,跑通后再扩展到多首。
6. 词云可视化
6.1 词云生成示例
拿到过滤后的词列表后,就可以生成词云了。词云本质上是对词频的视觉映射,字大的词出现次数多。
from wordcloud import WordCloud import matplotlib.pyplot as plt word_freq_text = " ".join(filtered_words) wc = WordCloud( width=800, height=400, background_color="white", max_words=100, colormap="viridis", random_state=42 ) wc.generate(word_freq_text) plt.figure(figsize=(10, 5)) plt.imshow(wc, interpolation="bilinear") plt.axis("off") plt.savefig("outputs/treasure_delicious_wordcloud.png", dpi=150)运行后,outputs目录下会生成一张词云图片。判断成功与否的标准很简单:图片能正常生成,高频词在视觉比例上明显大于低频词。
6.2 中文和韩文词云注意事项
如果歌词里保留中韩文,wordcloud需要指定字体文件,否则会显示成方块。Windows 上可以采用如下方式:
wc = WordCloud( font_path="C:/Windows/Fonts/msyh.ttc", width=800, height=400, background_color="white" )macOS 可以用/System/Library/Fonts/AppleSDGothicNeo.ttc这类韩文字体路径。具体字体路径按本机实际情况调整。
6.3 词云的局限
词云只能看整体词频分布,看不出句子级别的上下文。比如 “not good” 和 “good” 被拆成两个词后,词频上无法体现否定关系。所以词云适合快速感知主题,不适合做情感判断。情感判断需要下一节的句子级情感分析。
7. 歌词情感分析初探
7.1 VADER 情感分析模型
VADER 是专门针对英文社交媒体和短文本设计的情感分析模型,对歌词这种短句效果不错。它的输出是四个分数:positive、neutral、negative、compound。compound 是综合分,大于 0.05 偏向积极,小于 -0.05 偏向消极。
安装方式和示例:
pip install vaderSentimentfrom vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer analyzer = SentimentIntensityAnalyzer() sentence = "D to the E to the L I C I O U S delicious" scores = analyzer.polarity_scores(sentence) print(scores)输出示例:
{"neg": 0.0, "neu": 1.0, "pos": 0.0, "compound": 0.0}这个结果说明这句话在 VADER 眼中基本是中性,因为字母拼写本身没有明显情感词。如果歌词里有 “love”“sweet”“good” 这类词,正向分数会明显上升。
7.2 对整首歌做逐行情感分析
逐句分析比整段分析更合理。先把歌词按行切分,去掉空行,然后对每一行算分:
import pandas as pd def analyze_lyrics_sentiment(lyrics_text: str) -> pd.DataFrame: analyzer = SentimentIntensityAnalyzer() lines = [l.strip() for l in lyrics_text.splitlines() if l.strip()] records = [] for line in lines: scores = analyzer.polarity_scores(line) records.append({ "line": line, "compound": scores["compound"], "pos": scores["pos"], "neu": scores["neu"], "neg": scores["neg"] }) return pd.DataFrame(records) df_sentiment = analyze_lyrics_sentiment(lyrics_text) print(df_sentiment.describe())describe()会输出 compound 等字段的均值、标准差、最大最小值。均值可以粗略代表整首歌的情感基调,但要注意:一句特别负面的歌词会拉低均值,不能只凭均值下结论。
7.3 情感分布可视化
把每条歌词的情感倾向分类,画一个柱状图:
import matplotlib.pyplot as plt def classify_sentiment(score): if score > 0.05: return "positive" if score < -0.05: return "negative" return "neutral" df_sentiment["sentiment"] = df_sentiment["compound"].apply(classify_sentiment) sentiment_counts = df_sentiment["sentiment"].value_counts() plt.figure(figsize=(6, 4)) sentiment_counts.plot(kind="bar", color=["#4CAF50", "#F44336", "#9E9E9E"]) plt.title("Lyrics Sentiment Distribution") plt.xlabel("Sentiment") plt.ylabel("Line Count") plt.tight_layout() plt.savefig("outputs/lyrics_sentiment_distribution.png", dpi=150)柱状图能直观看出整首歌里积极、中性和消极句子的数量比例。如果分析对象是多个成员的歌词片段,还可以按成员分组统计,但前提是你对歌词行做了归属标注。
8. 批量任务与数据流水线
8.1 批量处理多首歌曲
做批量任务之前,先把单曲处理逻辑封装成函数:输入是歌词文件路径,输出是一个字典,包含该文件的歌名、总词数、去重词数、情感平均分、Top 词列表等字段。然后把所有文件一次性处理。
from pathlib import Path import pandas as pd def analyze_one_file(txt_file: Path) -> dict: text = txt_file.read_text(encoding="utf-8") words = clean_lyrics_to_words(text) filtered_words = [w for w in words if w not in stopwords and len(w) > 1] counter = Counter(filtered_words) df_sentiment = analyze_lyrics_sentiment(text) top_words = [w for w, _ in counter.most_common(10)] return { "file": txt_file.name, "total_words": len(words), "unique_words": len(set(filtered_words)), "avg_compound": df_sentiment["compound"].mean(), "top_words": "|".join(top_words) } rows = [] for txt_file in Path("./lyrics").glob("*.txt"): try: rows.append(analyze_one_file(txt_file)) print("处理完成:", txt_file.name) except Exception as e: print("处理失败:", txt_file.name, e) df = pd.DataFrame(rows) df.to_csv("outputs/lyrics_summary.csv", index=False, encoding="utf-8-sig") print(df)这段代码会把lyrics目录下所有 txt 歌词文件跑一遍,生成一个汇总 CSV。每首歌一行,包含词数、去重词数、情感平均分和 Top 10 词。后续可以用 Excel 打开,也可以继续画散点图或做对比分析。
8.2 批量任务的设计建议
批量处理时最怕两个问题:一个是某个文件编码不对导致脚本中断,另一个是请求歌词接口时频率过高被限流。代码里已经用try...except包住了单文件处理,保证单个文件失败不会中断整个流程。
如果是调用 API 批量获取歌词,建议每次请求之间加 0.5 到 1 秒的延时,同时把成功和失败的请求都记入日志:
import time import logging logging.basicConfig(level=logging.INFO, filename="batch.log", filemode="a") time.sleep(0.8) # 控制请求频率,避免触发限流 logging.info("processed %s", txt_file.name)8.3 把分析脚本封装成接口
如果后续想给这段分析脚本接一个 Web 页面,可以用 FastAPI 包一层。下面是一个最简单的接口示例,实际项目需要加请求校验和错误处理:
from fastapi import FastAPI, HTTPException from pydantic import BaseModel app = FastAPI() class LyricsRequest(BaseModel): text: str @app.post("/analyze") def analyze_endpoint(req: LyricsRequest): if not req.text.strip(): raise HTTPException(status_code=400, detail="empty lyrics") words = clean_lyrics_to_words(req.text) filtered = [w for w in words if w not in stopwords] counter = Counter(filtered) return { "word_count": len(words), "top_words": counter.most_common(20), "wordcloud_path": "outputs/request_wordcloud.png" }启动服务需要安装 fastapi 和 uvicorn:
pip install fastapi uvicorn uvicorn main:app --host 127.0.0.1 --port 8000注意,这个接口接收的是请求里的歌词文本,不是直接从网络抓取歌词。把“分析能力”和“数据获取能力”分开设计,是更稳妥的架构。也避免了把抓取逻辑写进接口造成滥用风险。
9. 资源占用与性能观察
9.1 CPU 和内存占用
这套文本分析流程几乎不消耗 GPU。主要资源消耗来自三块:读取文件时的内存占用、pandas DataFrame 转换时的临时内存、生成词云时的图像内存。处理几十首歌、每首歌几百行的歌词文本,内存占用通常在 1GB 以内,CPU 单核就能跑完。
如果你想观察资源占用,Windows 上用任务管理器看 Python 进程的“内存”列,macOS 和 Linux 上可以用htop。运行批量脚本时,重点关注内存是否持续增长。如果持续增长且不回落,说明某个文件特别大或某个数据结构存在泄漏,需要单文件排查。
9.2 文本长度对性能的影响
歌词文本很短,分析速度非常快。真正影响性能的是两个外部因素:一个是调用网络 API 时的时间消耗,另一个是生成高分辨率词云时的图像渲染消耗。把词云宽度从 800 调到 1600,渲染时间会明显增加,但视觉信息密度不一定提高。建议先用 800x400 跑通,需要高清图再调大。
9.3 进程残留问题
如果脚本中途异常退出,可能会留下 Python 进程。Windows 上可以这样处理:打开任务管理器,找到 python 进程,结束任务。macOS/Linux 上使用kill命令:
ps aux | grep python kill -9 <pid>批量脚本建议加上日志输出,这样即使某个文件处理失败,也能通过日志定位到具体文件,而不是整个任务卡住。
10. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 词云中文/韩文显示为方块 | 缺少对应字体文件 | 检查WordCloud的font_path | 指定系统字体路径,Windows 用msyh.ttc,macOS 用韩文字体 |
| 歌词清洗后单词全部丢失 | 正则把非英文字母全过滤了 | 打印words列表前 50 个 | 确认歌词是否以英文为主;如需保留韩文,扩充分词规则 |
| CSV 在 Excel 里中文乱码 | 编码使用了默认的 UTF-8 | 用文本编辑器检查文件头 | 保存 CSV 时指定encoding="utf-8-sig" |
| API 歌词请求返回 403 | 缺少请求头或没有注册权限 | 打印响应头 | 补全User-Agent和Authorization,确认 API Key 是否有效 |
| 批量任务中途卡住 | 某个文件编码异常或网络请求超时 | 查看日志和当前输出文件 | 用try...except包住单文件处理,加timeout参数 |
| vaderSentiment 安装失败 | Python 版本或依赖冲突 | 查看 pip 安装日志 | 改用nltk.sentiment.vader替代 |
| 词频 Top 词全是 the/a/and | 停用词表覆盖不足 | 打印filtered_words前 50 个 | 扩充停用词集合 |
| 情感分析结果全部为中性 | 歌词以拼写、拟声词为主 | 抽查几行调用方输入 | 改用带上下文的模型,或逐句人工复核 |
10.1 依赖安装失败的处理思路
有些依赖包在 Windows 上安装时会遇到编译问题,尤其是wordcloud。这种情况下不要硬编译,优先尝试用conda安装,或下载对应系统的 wheel 文件。nltk首次使用时如果提示缺少资源,需要下载对应的语料包:
import nltk nltk.download("stopwords")注意,资源下载和首次运行需要网络连接,如果下载失败,就继续使用自己定义的停用词集合,不依赖 nltk 数据包。
10.2 识别数据源字段变化
歌词 API 返回的字段可能和你预期不一致。建议每次请求后先打印 JSON 结构,确认歌词字段名,而不是直接写死索引。代码里用resp.json()结合.get()做容错,比直接用["lyrics"]安全得多:
data = resp.json() lyrics = data.get("lyrics") or data.get("text") or ""在没有明确 API 文档的情况下,这是最稳妥的字段获取方式。
11. 最佳实践与合规建议
11.1 先小后大,保留最小可运行配置
第一次分析不要直接跑 50 首歌。把单曲清洗、词频表、词云、情感分析四步作为一个最小闭环,确认每一层输出都是预期结果,再扩展到多歌曲批量处理。保留一个input_sample.txt和一个run_minimal.py,方便以后快速复现。
11.2 分目录管理数据和输出
建议把歌词原文、分析结果、图片分别放三个目录,不要把中间结果和原始数据混在一起。尤其是批量任务,如果中途失败,你需要能快速定位哪些文件已经处理完、哪些还没处理。常见的做法是用输出文件的文件名命名与输入一致的 CSR,并配上日志记录处理时间。
11.3 版权与隐私合规
歌词文本受版权保护,这是不能忽略的问题。个人学习和非商业研究场景下,分析歌词、展示词频和情感分布相对安全;但把歌词全文整理成数据集对外发布、商业化使用,可能存在版权风险。如果你的分析涉及艺人姓名、肖像或非公开的演出片段,必须确认授权范围。
涉及“粉丝向内容分析”时,不要做误导性结论。词云和情感分析只能反映歌词文本特征,不能代表艺人的真实性格或表演水平。发布分析结果时,尽量用客观描述,不要用“某成员情感更负面”这类容易引发误解的标题。
11.4 接口服务的访问控制
如果按照上一节的 FastAPI 示例启动服务,要注意这个服务默认没有鉴权,任何能访问到端口的人都能提交歌词文本。如果只在本机调试,绑定127.0.0.1即可:
uvicorn main:app --host 127.0.0.1 --port 8000如果需要给外部访问,要加 Token 校验,并限制单次请求的文本长度,避免有人提交超长文本拖垮进程。
12. 总结与下一步
这个方案最值得尝试的一点,是让你用最朴素的文本分析手段,把一首 K-pop 歌词变成词频表、词云和情感分布图。整个过程不需要 GPU,不需要下载大模型,一套 Python 脚本就能跑完,既适合练手,也适合临时做一个粉丝向的数据分析小项目。
最先应该验证的是清洗函数和词频统计。用 TREASURE 的歌词文本作为输入,看过滤掉停用词后得到的高频词是否符合直觉,确认这一层逻辑没问题,后面的词云和情感分析才有意义。
最容易踩的坑有三个:一是歌词数据源的字段或格式变化导致清洗结果为空,二是词云中文/韩文字体未配置导致乱码,三是批量任务没有日志和异常隔离,一个文件出问题就中断全部流程。
后续可以扩展的方向很明确:把多首歌曲的 CFG 汇总后做成员歌词风格对比;把歌词情感分布和公开音源数据做相关性分析;或者把同一首歌的中英韩多版本文本放到一起做跨语言词频对比。再往后,如果你愿意接一个可视化前端,这套分析逻辑可以直接包装成一个小型 Web 服务,给粉丝群自用。