简介:这份资源是面向高校学生与 Python 数据分析初学者的结课项目实战包,以网易云音乐歌单为分析对象,解决从数据获取到可视化呈现的完整流程问题,适合作为课程作业参考或数据分析入门练手案例。压缩包共 39 个文件,约 10.53MB,包含 12 个 py 源码、11 个 pyc 编译文件、7 张 png 图表、3 个 csv 数据文件,以及 md 说明、ttf 字体、pdf 报告等,覆盖代码、数据与成果展示三类内容。项目借助 numpy、pandas、matplotlib、requests、squarify、jieba、wordcloud、bs4 等第三方模块,完成歌单数据的抓取与清洗,并输出评论、收藏、播放、贡献、分布等数量图及词云,最后给出歌单优化建议。已有 3342 人学习,读者可据此理解数据分析项目的目录组织与模块分工,掌握爬虫、分词、词云与统计图表的组合用法,并借鉴其分析思路与结论表达方式。
1. 从一份网易云歌单出发:这套 Python 数据分析作业到底能跑出什么
你可能遇到过这种场景:课程设计要交一份数据分析作业,手头有 Python 环境,但不知道拿什么数据练手。这份「基于 Python 数据可视化的网易云音乐歌单分析系统」就是冲着这个需求来的——它把歌单数据抓取、清洗、统计、可视化串成一条完整链路,适合正在学 python 数据分析、需要交编程作业、又想顺便把 pandas 和 matplotlib 摸熟的人。它不教你从零写爬虫框架,而是给你一套能直接跑通的分析流程:读数据、算指标、出图表、拼报告。歌单本身是个很好的切入点,字段直观(歌名、歌手、时长、热度),数据量适中,跑起来不卡机器,结果又能一眼看懂。如果你正卡在「有工具没场景」的阶段,这份资源能让你把 python 数据分析与可视化 的课本知识真正落到一次可交付的作业上。
2. 数据从哪来、字段怎么定:歌单分析的输入层设计
2.1 为什么选歌单而不是评论或播放记录
做数据分析作业,第一步不是写代码,是决定数据源。网易云音乐可拿到的公开数据里,歌单是性价比最高的:评论数据字段少、情感分析门槛高;播放记录涉及个人隐私拿不到;而歌单天然带结构化字段,一首歌对应一行,歌手、时长、热度都是现成的数值或类别,清洗成本低。常见做法是先用 requests 拉取歌单详情接口,把 JSON 落成 CSV,后续所有分析都基于这份本地文件,避免每次跑脚本都重新请求。这样做的另一个好处是作业可复现——老师拿到你的代码和 CSV,不联网也能跑出同样的图。
歌单数据的核心字段一般包括:歌曲名、歌手、专辑、时长(毫秒)、热度值、评论数。其中时长和热度是数值型,直接进统计;歌手和专辑是类别型,适合做 Top N 排行。选这套字段的理由是它们能撑起四类基础图表:柱状图看歌手分布、饼图看时长区间占比、散点图看热度与评论数的关系、折线图看歌单内歌曲热度走势。字段不用多,够画出四张图、讲清一个结论,作业就立住了。
2.2 用 pandas 读数据并做第一轮体检
拿到 CSV 之后别急着画图,先做数据体检。这一步能帮你提前发现空值、类型错误和重复行,省得后面图表出来是歪的。
import pandas as pd # 读取歌单数据,指定编码防止中文乱码 df = pd.read_csv("playlist.csv", encoding="utf-8-sig") # 第一轮体检:看形状、类型、空值 print("数据形状:", df.shape) print("字段类型:\n", df.dtypes) print("空值统计:\n", df.isnull().sum()) # 时长从毫秒转成秒,方便后续分区间 df["duration_s"] = df["duration"] / 1000 # 去掉歌名重复的行,保留第一条 df = df.drop_duplicates(subset=["song_name"], keep="first") print("去重后形状:", df.shape)这段代码的逻辑是:先确认数据规模,再检查每个字段的类型是否符合预期(比如热度应该是 int 或 float,如果读成 object 说明有脏字符),然后统计空值分布。encoding="utf-8-sig"是为了处理带 BOM 的中文 CSV,不加这个参数经常出现第一列字段名带乱码。时长除以 1000 是因为原始接口返回的是毫秒,人看秒更直观。去重按歌名做,因为同一首歌可能在歌单里出现多次,重复会拉高排行统计的偏差。
参数上唯一需要按你实际数据调整的是subset,如果歌名有重名但歌手不同,可以改成subset=["song_name", "artist"]。跑完这一步,你应该得到一份行数略少于原始数据、字段类型干净、没有关键空值的 DataFrame,这就是后续所有分析的底座。
3. 统计与可视化:把歌单数据变成四张能交作业的图
3.1 歌手排行与时长分布的统计口径
统计部分最容易翻车的地方不是代码,是口径。比如「歌手排行」到底按歌曲数量排还是按总热度排,结果完全不同。我一般会两个都算,作业里分别说明,显得分析有层次。
# 按歌曲数量统计歌手排行 artist_count = df["artist"].value_counts().head(10) # 按总热度统计歌手排行 artist_heat = df.groupby("artist")["popularity"].sum().sort_values(ascending=False).head(10) # 时长分区间:短于3分钟、3到5分钟、超过5分钟 bins = [0, 180, 300, float("inf")] labels = ["短歌(<3min)", "标准(3-5min)", "长歌(>5min)"] df["duration_bin"] = pd.cut(df["duration_s"], bins=bins, labels=labels) duration_dist = df["duration_bin"].value_counts()value_counts()默认按频次降序,取 head(10) 就是 Top10 歌手。groupby加sum是按热度聚合,适合看谁的作品整体更受欢迎。pd.cut做分箱时,bins的边界要覆盖数据最小值到最大值,float("inf")表示不设上限,避免有超长歌曲被漏掉。labels 用中文是为了图表直接可读,不用再映射。这三个统计结果分别对应柱状图、柱状图和饼图,数据量小,跑起来秒出。
3.2 用 matplotlib 出图并处理中文显示
可视化环节最经典的坑是中文变方块。matplotlib 默认字体不含中文,必须手动指定。下面这段是出图模板,改数据源就能复用。
import matplotlib.pyplot as plt # 指定中文字体,Windows 用 SimHei,Mac 用 Arial Unicode MS plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False # 解决负号显示为方块 fig, axes = plt.subplots(2, 2, figsize=(12, 10)) # 图1:歌手歌曲数量 Top10 artist_count.plot(kind="bar", ax=axes[0, 0], color="#4C72B0") axes[0, 0].set_title("歌手歌曲数量 Top10") axes[0, 0].set_ylabel("歌曲数") # 图2:时长区间分布 duration_dist.plot(kind="pie", ax=axes[0, 1], autopct="%1.1f%%") axes[0, 1].set_title("歌曲时长分布") axes[0, 1].set_ylabel("") # 图3:热度与评论数散点 axes[1, 0].scatter(df["popularity"], df["comment_count"], alpha=0.5) axes[1, 0].set_xlabel("热度") axes[1, 0].set_ylabel("评论数") axes[1, 0].set_title("热度与评论数关系") # 图4:歌单内热度走势 axes[1, 1].plot(df["popularity"].values, color="#C44E52") axes[1, 1].set_title("歌单热度走势") axes[1, 1].set_xlabel("歌曲序号") axes[1, 1].set_ylabel("热度") plt.tight_layout() plt.savefig("playlist_analysis.png", dpi=150) plt.show()font.sans-serif设成 SimHei 是 Windows 下的通用做法,Mac 用户改成Arial Unicode MS,Linux 服务器上如果没有中文字体,可以提前装一个思源黑体再指定字体名。axes.unicode_minus = False这行不加,负号会显示成方块,虽然歌单数据一般没负数,但养成习惯没坏处。subplots(2, 2)一次排四张图,tight_layout()自动调整间距防止标题重叠,dpi=150保证导出图片够清晰,交作业打印也不糊。散点图加alpha=0.5是为了重叠点能看出密度,不然点全糊成一团。
3.3 把图表拼成一份能交的作业报告
图出来只是半成品,作业要的是有结论的报告。常见做法是用 matplotlib 的savefig导出图片,再在 Markdown 或 Word 里写文字说明。每张图配三句话:这张图看什么、数据说明了什么、和歌单主题有什么关系。比如时长分布饼图如果显示标准时长占 70%,就可以写「该歌单以 3 到 5 分钟的主流流行歌曲为主,符合大众听歌习惯」。这一步不需要额外代码,但决定了作业是「跑了个脚本」还是「做了次分析」。如果想让报告更自动,可以用df.describe()导出统计摘要,把均值、中位数、最大最小值贴进报告,数据支撑更硬。
4. 避坑与排查:跑这套分析时最容易翻车的五个地方
4.1 中文乱码:图表方块和 CSV 读歪
现象是图表标题全是方块,或者读 CSV 时第一列字段名带\ufeff。原因是 matplotlib 没指定中文字体,以及 CSV 带 BOM 头。解决办法是出图前设plt.rcParams["font.sans-serif"],读文件用encoding="utf-8-sig"。如果换了环境字体名不对,用matplotlib.font_manager查一下系统里有哪些中文字体再填。
4.2 字段类型错误:热度被读成字符串
现象是df["popularity"].sum()报错或结果变成字符串拼接。原因是 CSV 里热度列混了非数字字符,pandas 推断成 object。解决办法是读数据时加dtype={"popularity": float}强制指定,或者读完后用pd.to_numeric(df["popularity"], errors="coerce")转换,coerce会把无法转换的变成 NaN,再决定是删还是填。
4.3 分箱边界漏数据:最长歌曲没进任何区间
现象是value_counts()的总数小于 DataFrame 行数。原因是pd.cut的 bins 上限设小了,超过上限的值变成 NaN。解决办法是把最后一个边界设成float("inf"),或者先用df["duration_s"].max()看一眼最大值再定边界。这个坑很隐蔽,因为不报错,只是数据悄悄少了。
4.4 去重口径过宽:不同歌手的同名歌被误删
现象是去重后行数掉得比预期多。原因是drop_duplicates只按歌名去重,把不同歌手的同名歌也删了。解决办法是subset加上歌手字段,subset=["song_name", "artist"]。如果连翻唱版本都要保留,就再加专辑字段,口径越细保留越多。
4.5 图片保存空白:show 和 savefig 顺序反了
现象是savefig出来的图片是空白。原因是在某些后端下plt.show()会清空画布,之后再保存就没了。解决办法是把savefig放在show之前,或者干脆不调show,直接保存。这个坑在 Jupyter 里不明显,换成脚本跑就暴露了。
5. 进阶玩法:让这份作业从及格变成有记忆点
基础四张图能交差,但想让作业被记住,得加一点别人没做的。我一般会从两个方向切:一是加一层「时间维度」,如果歌单数据里能拿到歌曲发行年份,就按年份做折线图,看这个歌单是怀旧型还是追新型;二是加一层「文本维度」,把歌手名字做词云,或者把歌名里的高频词提出来,用 jieba 分词后统计。这两个方向都不难,但能让报告从「统计了数据」变成「发现了点什么」。
词云的做法是先装wordcloud和jieba,把歌名拼成一个长字符串,分词后过滤掉单字和停用词,再生成词云图。参数上font_path必须指定中文字体路径,否则又是方块;max_words控制显示词数,一般 50 到 100 够用;background_color设成白色方便打印。年份折线图更简单,groupby年份后count再plot就行,但前提是数据里有年份字段,没有的话这一步跳过,别硬编。
验证方法上,我习惯跑完所有图后回头对一遍总数:每张图的数据加起来应该等于去重后的行数,对不上就说明有数据在某个环节被漏掉或重复计算。这个习惯是从一次作业翻车里养成的——当时饼图加起来只有 85%,查了半天发现是分箱边界漏了长尾数据。从那以后我每次出完图都强制走一遍总数核对,宁可多花五分钟,也不想到答辩时被问住。
如果想让代码更工程化,可以把读数据、清洗、统计、出图拆成四个函数,主流程只负责调用。这样改数据源时只动读数据那一步,其他不用碰。函数命名用动词开头,比如load_playlist、clean_data、compute_stats、plot_charts,别人接手一眼能看懂。这套结构不复杂,但能体现你不只是会跑脚本,还知道怎么组织代码,作业评分上是个加分项。
希望帮到你。
本文还有配套的精品资源,点击获取