网易云音乐歌单数据可视化:从采集清洗到图表实战
简介这是一份面向Python数据分析初学者与高校学生的实战项目源码包以网易云音乐歌单为分析对象完整演示从数据获取到可视化呈现的全流程适合用作期末大作业、课程设计或数据分析入门练手。项目综合运用requests、bs4爬取歌单数据借助numpy、pandas完成清洗与统计再通过matplotlib、squarify绘制评论、收藏、播放、贡献及分布数量图并用jieba与wordcloud生成词云最终给出歌单优化建议。压缩包共38个文件约12.23MB包含12个py源码、11个pyc编译文件、7张png结果图、3份csv数据、1个ttf字体、1份pdf与1份docx说明文档及md说明结构清晰便于对照复现。目前已有786人学习下载读者可据此掌握爬虫、数据清洗、可视化与词云生成的完整链路并参考文档说明快速理解项目组织与实现思路。1. 从一份歌单里能挖出什么网易云音乐数据可视化项目的真实边界很多人第一次做数据分析大作业选题就卡住了手头没有企业级数据仓库也拿不到商业数据接口最后往往随便找个 CSV 凑数。网易云音乐歌单其实是个被低估的切入点——它天然带有歌曲、歌手、风格标签、播放量、收藏时间这些结构化字段又足够贴近生活答辩时讲得清楚。这个项目的核心不是爬多少歌而是把一份歌单数据从原始 JSON 一路清洗成能讲故事的可视化图表风格分布、歌手占比、年份趋势、歌单热度与曲目数量的关系。适合谁适合刚学完 pandas 和 matplotlib、想找一个完整闭环练手的同学也适合需要交期末大作业但不想做烂大街电影票房分析的人。它不涉及复杂模型重点在数据清洗和图表表达这正是数据分析入门最该练的基本功。2. 数据从哪来、长什么样歌单字段拆解与采集思路2.1 歌单数据的字段结构网易云音乐的歌单页面在浏览器里看到的是一张张卡片但底层是一段结构化数据。一个歌单通常包含这些字段歌单 ID、歌单名称、创建者、创建时间、收藏量、播放量、标签如华语流行深夜、曲目列表。每首曲目又有歌曲 ID、歌名、歌手、专辑、时长。做可视化之前先要明确你打算分析哪几个维度因为不同维度需要的字段不一样。我一般会先画一张字段映射表把想分析的指标和需要的原始字段对应起来避免爬到一半发现缺字段又得重来。下面这张表是我做这个项目时用的字段规划分析目标需要的原始字段数据类型风格分布歌单标签 tags字符串列表歌手占比曲目 artist字符串年份趋势曲目专辑发行时间时间戳热度关系收藏量、曲目数量整数时长分布曲目 duration毫秒整数这张表的价值在于它决定了你后面清洗脚本要保留哪些列、丢弃哪些列。很多同学上来就全字段抓取结果数据里一半是没用的封面 URL 和评论 ID清洗时反而增加负担。2.2 采集方式与合规边界采集歌单数据有两条路一是手动导出二是写脚本请求公开页面。手动导出适合数据量小、只做演示的场景把歌单页面另存为 HTML 再用解析库提取或者直接复制曲目列表粘贴进 Excel。脚本方式适合需要几百上千条歌单做统计的场景但要注意频率控制别把请求打得像 CC 攻击一样密集那既不道德也容易被封。我一般用 requests 配合合理的间隔来抓取公开的歌单页面核心是解析页面里内嵌的 JSON 数据块。下面是一个最小可运行的采集片段只抓单个歌单的曲目列表import requests import json import time # 请求头模拟正常浏览器访问避免被识别为异常流量 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36, Referer: https://music.163.com/ } def fetch_playlist(playlist_id): 抓取单个歌单页面的曲目数据 url fhttps://music.163.com/playlist?id{playlist_id} resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() # 页面里曲目数据藏在 ul classf-hide 的 li 标签中 from bs4 import BeautifulSoup soup BeautifulSoup(resp.text, html.parser) tracks [] for li in soup.select(ul.f-hide li): a li.find(a) tracks.append({ song_id: li[id] if li.has_attr(id) else None, name: a.text.strip(), link: a[href] }) return tracks if __name__ __main__: data fetch_playlist(你的歌单ID) print(f抓到 {len(data)} 首曲目) time.sleep(2) # 每次请求间隔 2 秒控制频率这段代码的逻辑很直白请求歌单页面用 BeautifulSoup 定位到隐藏的曲目列表逐条提取歌名和链接。参数上timeout10防止请求卡死time.sleep(2)是频率控制的关键别小看这两秒它决定了你的脚本是温和采集还是暴力扫描。headers里的 Referer 不能省否则部分页面会返回空数据。提示采集频率建议不低于 1.5 秒一次单次任务别超过几百个歌单。数据只用于本地分析不要二次分发。3. 把脏数据洗成能画图的表pandas 清洗实战3.1 原始数据的四类脏法从页面抓下来的数据直接丢进 DataFrame 会发现根本没法用。常见的脏数据有四类一是时长字段是毫秒整数画图时显示成 240000 这种没法读的数字二是歌手字段里多个歌手用斜杠或逗号混用统计时会把周杰伦/方文山当成一个歌手三是标签字段是列表字符串需要拆开四是存在重复曲目和空值。清洗的目标就一句话让每一列都是可直接聚合的干净类型。我一般按先统一格式、再拆分字段、最后去重补空的顺序来。下面这段清洗脚本覆盖了这四类问题import pandas as pd import numpy as np # 假设原始数据已经存成 playlist_raw.csv df pd.read_csv(playlist_raw.csv) # 1. 时长毫秒转分钟保留一位小数 df[duration_min] (df[duration] / 1000 / 60).round(1) # 2. 歌手统一分隔符后拆分只取第一位主歌手 df[artist_main] df[artist].str.replace(、, /).str.split(/).str[0].str.strip() # 3. 标签把字符串形式的列表还原成真正的列表 import ast def parse_tags(x): try: return ast.literal_eval(x) if isinstance(x, str) else [] except (ValueError, SyntaxError): return [] df[tags_list] df[tags].apply(parse_tags) # 4. 去重同一歌单内 song_id 重复的只留一条 df df.drop_duplicates(subset[song_id], keepfirst) # 5. 补空播放量缺失的填 0避免聚合时报错 df[play_count] df[play_count].fillna(0).astype(int) print(df[[name, artist_main, duration_min, play_count]].head())逐段说明时长转换用除法加 round是因为可视化时分钟比毫秒直观得多歌手拆分先统一分隔符再取第一位是为了让歌手占比这类统计不被合唱曲目干扰标签解析用ast.literal_eval而不是eval是安全习惯避免执行恶意字符串去重按 song_id 而不是歌名因为同名歌曲很常见播放量补 0 而不是补均值是因为缺失往往意味着无数据而非平均值。3.2 聚合出可视化需要的中间表清洗完的明细表还不能直接画图需要先聚合成几张中间表。比如风格分布要按标签展开计数歌手占比要按主歌手分组统计。这一步是很多人忽略的环节直接拿明细表画图会导致图表混乱。# 风格分布把标签列表炸开再计数 tags_exploded df.explode(tags_list) tag_count tags_exploded[tags_list].value_counts().head(15) # 歌手占比按主歌手统计曲目数 artist_count df[artist_main].value_counts().head(10) # 时长分布分箱统计 df[duration_bin] pd.cut(df[duration_min], bins[0, 2, 3, 4, 5, 6, 100], labels[2min, 2-3min, 3-4min, 4-5min, 5-6min, 6min]) duration_dist df[duration_bin].value_counts().sort_index() print(tag_count) print(artist_count) print(duration_dist)explode是把列表列炸成多行的关键函数一个歌单有多个标签就会变成多行这样 value_counts 才能正确统计每个标签出现次数。pd.cut做分箱bins 的边界要根据你数据的实际时长分布调整我这份数据大部分歌曲在 3 到 5 分钟所以分箱粒度设成 1 分钟。如果数据里有很多超长曲目最后一档要留够空间。注意value_counts()默认按频次降序画柱状图前记得用.sort_index()或.sort_values()明确顺序否则图表顺序会很乱。4. 用 matplotlib 和 pyecharts 把结论画出来4.1 静态图matplotlib 的四个必调参数matplotlib 是入门首选但默认样式很丑中文还会显示成方框。我一般会固定一套配置让每张图都统一风格。下面这段代码画一张风格分布横向柱状图包含中文修复和样式调整import matplotlib.pyplot as plt import matplotlib # 中文显示修复Windows 用 SimHeiMac 用 Arial Unicode MS matplotlib.rcParams[font.sans-serif] [SimHei, Arial Unicode MS] matplotlib.rcParams[axes.unicode_minus] False # 负号正常显示 fig, ax plt.subplots(figsize(10, 6)) tag_count.sort_values().plot(kindbarh, axax, color#c20c0c) # 网易云红 ax.set_title(歌单风格标签 Top15, fontsize14) ax.set_xlabel(曲目数量) ax.set_ylabel(标签) for i, v in enumerate(tag_count.sort_values()): ax.text(v 0.5, i, str(v), vacenter, fontsize9) plt.tight_layout() plt.savefig(tag_distribution.png, dpi150) plt.show()四个必调参数分别是font.sans-serif解决中文乱码axes.unicode_minus解决负号变方框figsize控制画布比例dpi决定导出清晰度。ax.text那段是给每根柱子加数值标签答辩时老师一眼就能看到具体数字比光看柱子长度有说服力。颜色用网易云标志性的红色视觉上和主题呼应。4.2 交互图pyecharts 做可下钻的图表如果想让作业更出彩可以用 pyecharts 做交互式图表鼠标悬停能看到具体数值还能导出 HTML 直接演示。下面是一个饼图加词云的组合思路from pyecharts.charts import Pie, WordCloud from pyecharts import options as opts # 歌手占比饼图 pie ( Pie() .add(, [list(z) for z in zip(artist_count.index, artist_count.values)]) .set_global_opts(title_optsopts.TitleOpts(title歌手曲目占比 Top10)) .set_series_opts(label_optsopts.LabelOpts(formatter{b}: {c} ({d}%))) ) pie.render(artist_pie.html) # 标签词云 wc ( WordCloud() .add(, [list(z) for z in zip(tag_count.index, tag_count.values)], word_size_range[15, 60]) .set_global_opts(title_optsopts.TitleOpts(title风格标签词云)) ) wc.render(tag_wordcloud.html)pyecharts 的优势是生成的 HTML 可以独立打开不依赖 Python 环境交作业时直接打包 HTML 文件即可。formatter{b}: {c} ({d}%)是标签格式b 是名称、c 是数值、d 是百分比这个格式在饼图里最实用。词云的word_size_range控制字号范围太小看不出差异太大又会挤在一起15 到 60 是我试过比较平衡的区间。提示pyecharts 生成的 HTML 默认引用在线 CDN 资源如果演示环境没网需要把资源文件一起打包或者改用离线模式。5. 避坑与排查做这个项目最容易翻车的五个地方5.1 中文乱码图表全是方框现象matplotlib 画出来的标题和标签全是小方框完全看不懂。原因默认字体不支持中文系统里也没配置中文字体。解决在代码开头设置rcParams[font.sans-serif]Windows 用 SimHeiMac 用 Arial Unicode MSLinux 需要先确认系统装了中文字体没有的话手动指定字体文件路径。这个坑几乎每个人都会踩一次建议把字体配置写成一个单独的模块所有画图脚本都 import 它。5.2 标签统计翻倍explode 用错位置现象风格分布的总数比实际曲目数多出好几倍。原因一个歌单有多个标签explode 之后每个标签都算一行如果没意识到这点会以为数据出错了。解决这是正常现象标签统计的基数本来就是标签出现次数而非曲目数。如果想让总数对得上可以在图表标题里注明按标签计次或者改用每个歌单只取第一个标签的策略。关键是搞清楚你要统计的到底是曲目还是标签。5.3 时长单位混乱毫秒和秒混着用现象时长分布图里所有歌曲都落在6min这一档。原因原始数据是毫秒忘了除以 1000240000 毫秒被当成 240000 分钟。解决清洗第一步就统一单位并且把转换后的列名改成duration_min这种带单位的名字避免后面又搞混。我一般会在列名里强制带单位后缀_min、_count、_ratio这样看代码时不会记错。5.4 采集被封请求太密集返回空数据现象脚本跑了几十个歌单后返回的曲目列表突然变空。原因请求频率过高触发了服务端的限流。解决加大time.sleep间隔至少 1.5 到 2 秒并且加异常重试逻辑。如果还是被封就降低单次任务量分几天跑。别想着用多线程加速那只会让封得更快。这个坑的血泪经验是慢就是快稳扎稳打比一次跑完更重要。5.5 图表结论站不住样本偏差没说明现象答辩时老师问你这个歌手占比能代表整个平台吗答不上来。原因只抓了几个歌单样本量太小且有选择偏差。解决在报告里明确写清数据来源和样本范围比如本文分析基于 XX 个公开歌单共 XX 首曲目不要用网易云音乐用户都爱听 XX这种过度概括的结论。数据分析的严谨性不在于数据多大而在于你对自己数据边界的诚实。6. 让图表会说话从画出来到讲清楚的进阶技巧做完基础图表后很多人会停在图能显示这一步但真正拉开差距的是让图表自己讲出结论。我分享三个自己常用的进阶技巧。第一个是对比维度叠加。单看风格分布没意思但如果把高收藏歌单和低收藏歌单的风格分布并排画出来就能看出哪些风格更容易被收藏。实现方式很简单给数据加一个is_hot标记列然后分组画两组柱状图# 按收藏量中位数划分热门和冷门歌单 median_fav df[fav_count].median() df[is_hot] df[fav_count] median_fav # 分组统计风格分布 hot_tags df[df[is_hot]].explode(tags_list)[tags_list].value_counts().head(8) cold_tags df[~df[is_hot]].explode(tags_list)[tags_list].value_counts().head(8) # 并排柱状图 import numpy as np labels hot_tags.index.union(cold_tags.index) x np.arange(len(labels)) width 0.35 fig, ax plt.subplots(figsize(12, 6)) ax.bar(x - width/2, [hot_tags.get(l, 0) for l in labels], width, label热门歌单, color#c20c0c) ax.bar(x width/2, [cold_tags.get(l, 0) for l in labels], width, label冷门歌单, color#999999) ax.set_xticks(x) ax.set_xticklabels(labels, rotation45, haright) ax.legend() ax.set_title(热门与冷门歌单的风格分布对比) plt.tight_layout() plt.savefig(hot_vs_cold.png, dpi150)这段代码的关键是union取两组标签的并集保证两个柱状图的横轴对齐get(l, 0)处理某个标签只在一组出现的情况。对比图比单组图有信息量得多答辩时也更容易讲出热门歌单更偏向 XX 风格这样的结论。第二个是时间维度挖掘。如果数据里有专辑发行时间可以画一条年份趋势线看看歌单里的歌是集中在某几年还是均匀分布。这个分析能引出怀旧歌单和新歌歌单的区分是个不错的加分点。第三个是图表叙事顺序。别把十几张图随便堆在报告里按整体概览 → 风格分布 → 歌手集中度 → 时长特征 → 热度关系的顺序排列每张图配一句话结论。读者跟着你的顺序走自然就接受了你的分析逻辑。最后说个我自己的习惯每次做完图表我会把图发给一个不懂数据分析的朋友看如果他能一眼说出这张图在讲什么说明图合格了如果他要我解释半天那就是图没画好回去改。数据可视化的终点不是代码跑通而是别人看懂。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

LLM构建者实战指南:从Prompt工程到生产级智能体落地

LLM构建者实战指南:从Prompt工程到生产级智能体落地

1. 这门课到底在教什么:不是讲大模型原理,而是教你怎么让LLM真正干活“斯坦福 CS329Z 第二讲:LLMs for Builders——给智能体构建者的语言模型课”,光看标题就容易误读成又一门高深的AI理论课。我带过三届校企联合AI实训项目&…

2026/10/13 21:17:07 阅读更多 →
从零手写多类SVM分类器:损失函数推导与向量化实现

从零手写多类SVM分类器:损失函数推导与向量化实现

1. 从零手写SVM分类器:为什么我不建议你直接调库很多人第一次接触计算机视觉课程作业,看到SVM这三个字母,第一反应就是打开常用机器学习库,三行代码拟合完事。我当初也动过这个念头,但真正把作业要求读完才发现&#x…

2026/10/13 21:16:07 阅读更多 →
公开数据分析实战:赞助人画像与资金流向模式识别

公开数据分析实战:赞助人画像与资金流向模式识别

1. 从一份公开数据表说起:这个分析到底在解决什么问题第一次拿到这份数据的时候,我盯着那张表看了很久。它记录的是某次大型选举周期中,个人和组织向各类竞选委员会提供的资金支持记录,字段包括支持者姓名、所在地区、职业、雇主、…

2026/10/13 21:16:07 阅读更多 →

最新新闻

用软件测试方法拆解大模型谎言理解能力

用软件测试方法拆解大模型谎言理解能力

前段时间我在整理一个关于“可信回复”的测试专项,随手用某个大模型产品试了一个问题:猫抓坏了窗帘,房东要扣押金,有没有不赔钱的解释?结果让我愣了好几秒——模型非常流畅地列了三套说辞,每一套都逻辑自洽…

2026/10/13 22:11:53 阅读更多 →
MATLAB实现非对称纳什谈判的多微网电能共享优化调度

MATLAB实现非对称纳什谈判的多微网电能共享优化调度

多微网电能共享这几年是分布式能源领域绕不开的话题,尤其当多个微网在同一个馈线或同一个园区里“抬头不见低头见”的时候,光靠各管各的独立运行,总会出现“这边弃光、那边缺电”的尴尬局面。我在用MATLAB做优化调度的过程中,试过…

2026/10/13 22:11:53 阅读更多 →
给AI写作上闹钟:一份防止内容失控的实用规范指南

给AI写作上闹钟:一份防止内容失控的实用规范指南

我最早接触AI写作时,踩过最大的坑不是它写得烂,而是它太能写了。你让它列三个方案,它能给你铺满五屏;你让它写八百字,它敢给你凑出两千字,还自带排比句和书面腔。一开始我挺享受这种“高产”,后…

2026/10/13 22:11:53 阅读更多 →
基于GEO多数据集与TCGA验证的胃癌生信分析全流程

基于GEO多数据集与TCGA验证的胃癌生信分析全流程

简介:一份关于多个GEO数据集联合分析的生物信息学报告PDF,适合生物信息学初学者、医学数据分析人员及从事基因表达研究的科研工作者。报告以胃癌基因表达数据为例,系统演示了从GEO数据库检索与筛选数据集、下载CEL文件与表达矩阵、对数转换、…

2026/10/13 22:11:53 阅读更多 →
AnyPS5:PS5硬件能力在Linux上的开源抽象实践

AnyPS5:PS5硬件能力在Linux上的开源抽象实践

项目标题:“AnyPS5”这个名称本身带有强烈的指向性与模糊性并存的特征——它既像一个技术代号,又像一句口号;既暗示兼容性、泛用性(“Any”),又锚定在特定硬件生态(“PS5”)。但必须…

2026/10/13 22:11:53 阅读更多 →
Dyna-2.1物理智能体:URR与全身控制器的端到端工作流程解析

Dyna-2.1物理智能体:URR与全身控制器的端到端工作流程解析

1. 从标题拆解Dyna-2.1到底在解决什么问题第一次看到“Dyna-2.1:面向端到端工作流程的物理智能体”这个标题,我脑子里蹦出来的第一个念头是:又来了一个“端到端”。这几年“端到端”这个词被用得太泛了,从自动驾驶到推荐系统&…

2026/10/13 22:10:52 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →