ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于网易云歌单的Python数据分析与可视化实战

基于网易云歌单的Python数据分析与可视化实战 简介一份基于Python数据可视化的网易云音乐歌单分析系统源码与配套文档说明面向Python期末大作业和课程设计场景适合需要数据分析与可视化实战练手的初学者与高校学生。项目围绕歌单数据完成读取、清洗、统计分析与可视化展示功能模块完整代码注释详细下载部署后即可运行既能作为学习模板也能直接改造用于提交作业。压缩包共36个文件约8.48MB含12个Python源码文件、11个pyc编译文件、7张可视化效果图、3个csv数据文件以及字体、图片和README说明文档目录结构清晰便于理解整体流程。目前已有2910人学习下载。借助源码可掌握数据分析与可视化完整链路参考界面设计与结果呈现方式还有说明文档辅助上手是一份实用性强、门槛较低的课程设计参考资源。1. 为什么拿网易云歌单做数据分析大作业数据够真实技术栈够全期末大作业要求做一个Python数据分析与可视化项目时最怕的就是选题空泛爬下来的数据自己都不信图表做得再花哨也经不起答辩追问。网易云音乐歌单分析系统是个被验证过很多次的方向——歌单本身就是半结构化数据一首歌自带歌手、专辑、时长、热度、语种这些字段天然适合做清洗、分析和可视化。一套源码做下来能覆盖 requests 采集、Pandas 处理、Matplotlib/Seaborn/Pyecharts 出图这几条主线正好对应课程大纲里数据分析与可视化的核心考点答辩时也有的讲。适合计科、大数据、电商专业做课程设计或综合大作业的在校生也适合想练手完整数据分析流程的 Python 初学者。2. 从歌单API到数据仓库数据采集与落库的完整流程2.1 歌单数据从哪来三种来源怎么选做歌单分析第一步是拿到歌单里的歌曲列表。网易云音乐没有对外提供完全开放的数据接口从业余项目角度常见做法有三种第一种是直接调网页版接口。网易云的前端页面在加载歌单时会请求music.163.com/api/v6/playlist/detail这个地址返回 JSON 里包含歌单基本信息和歌曲列表。这种方式最简单requests 就能搞定也是大多数Python数据分析课程设计源码采用的方式。第二种是解析页面 HTML。打开歌单页面后首屏歌词列表其实就藏在 HTML 的script标签里用正则或 BeautifulSoup 能抠出来。优势是不用猜接口参数缺点是拿不全页面只渲染前 10 首必须滚动加载才能拿到完整列表解析成本高不建议作为主方案。第三种是自己构造 JSON 文件。如果你只是想把数据分析流程跑通也可以手动整理几十条歌曲信息存成 JSON。这样做稳定、零反爬风险但数据量太小做出来的图表说服力不够答辩时容易被问数据哪来的。我的建议是主用第一种辅以第二种做补充。下面的代码以网页版接口为例这是目前课程设计里最主流的做法代码结构清晰改起来也快。2.2 用 requests 采集歌单详情一套可直接改的最小代码先找一首歌单的 ID。在网易云音乐网页版打开任意歌单URL 里id后面的数字就是playlist_id。比如歌单链接是https://music.163.com/#/playlist?id3778678那3778678就是我们要传的参数。import requests import time def fetch_playlist_detail(playlist_id, cookie_str, retries3): url https://music.163.com/api/v6/playlist/detail params {id: playlist_id} 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/, Cookie: cookie_str, # 从浏览器登录态里复制 } for attempt in range(retries): try: resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() data resp.json() if data.get(code) 200: return data[playlist] else: print(f接口返回非 200: {data.get(code)}) except requests.RequestException as e: print(f第 {attempt 1} 次请求失败: {e}) time.sleep(2) # 失败后停 2 秒再重试 return None这段代码的逻辑很直白构造请求头和参数发起 GET 请求解析 JSON根据code字段判断接口是否正常。params里只需要传idn不传时接口默认返回歌单内全部歌曲传了反而可能截断。headers里三个字段缺一不可User-Agent伪装浏览器标识Referer告诉服务器请求来自网易云页面本身Cookie用于绕过未登录时的部分限制。参数调整建议timeout设 10 秒比较稳妥网络差可以放宽到 15retries设 3 次足够再多会拖慢整体采集time.sleep(2)是重试间隔不要改成 0否则连续失败时等于在加速封禁。拿到playlist字典后下一步是把里面的tracks列表转成结构化表格。import pandas as pd def playlist_to_dataframe(playlist): tracks playlist.get(tracks, []) if not tracks: return pd.DataFrame() rows [] for t in tracks: rows.append({ song_id: t.get(id), song_name: t.get(name), artist: /.join(a[name] for a in t.get(ar, [])), album: (t.get(al) or {}).get(name), duration_ms: t.get(dt), pop: t.get(pop, 0), fee: t.get(fee, 0), }) return pd.DataFrame(rows)这里有几个容易看懵的字段ar是歌手列表一首歌可能是多人合唱所以用/.join(...)拼成 周杰伦/阿信 这种格式al是专辑信息有的歌曲没有专辑需要先用or {}兜底否则t.get(al)返回None再取.get(name)会直接报错pop是网易云给歌曲打的平台热度值范围 0 到 100不是播放量但能反映歌曲相对热度fee表示收费状态0 免费1 VIP 可听。2.3 落库到 SQLite为什么不用 CSV采集结果直接存 CSV 当然能跑但课程设计做到后面会后悔多个歌单合并时字段对不齐、去重要写一堆 Pandas 逻辑、答辩现场演示时还会出现 CSV 被 Excel 占用导致写入失败的情况。用 SQLite 能把这些麻烦一次性省掉。import sqlite3 df playlist_to_dataframe(playlist) conn sqlite3.connect(music.db) df.to_sql(songs, conn, if_existsappend, indexFalse) conn.close() print(f已写入 {len(df)} 条记录)to_sql是 Pandas 自带的落库方法第一次运行用if_existsreplace第二次开始换成append这样多个歌单的数据能累积到同一张表里不会互相覆盖。indexFalse必须加否则 Pandas 会把行号也写进表里后续查询多出一个无意义的index列。为什么推荐 SQLite 而不是 MySQL大作业阶段不需要部署数据库服务SQLite 是一个文件拷到任何电脑都能打开答辩时换教室演示也不会因为数据库没启动而翻车。后面做去重和聚合查询时直接写 SQL 比在 Pandas 里merge更直观也方便在文档说明里展示数据层的完整设计。2.4 歌单数据字典拿到手后先确认哪些字段采集完成后先别急着画图把数据字典定下来。这套源码里最核心的字段如下字段名类型含义分析价值song_idint歌曲唯一 ID去重主键song_namestr歌曲名文本分析、词云artiststr歌手名多个用 / 分隔歌手分布、合作分析albumstr专辑名专辑聚合统计duration_msint时长单位毫秒时长分布、分桶popint平台热度值 0-100热度分桶、相关性分析feeint0 免费 1 VIP 2 付费免费率统计拿到数据后第一件事是df.info()和df.head()确认每个字段有没有解析出来。很多时候歌曲的dt字段在接口里是毫秒整数但有的歌单接口返回的 tracks 里混入了广告推荐歌曲字段结构不完全一致。先把数据字典定下来后面清洗和可视化才不会做到一半发现缺字段。3. 数据清洗与特征工程把歌单变成能分析的表格3.1 清洗四件套去重、补缺、类型转换、异常截断接口拿到的数据远没有想象中干净。同一个歌单可能出现重复歌曲album字段可能为空duration_ms可能混入字符串还有一些时长异常短的占位歌曲。清洗这一步不做好后面图表全是错的答辩时一眼就能被看出是应付。# 1. 去重同一首歌在一个歌单里只保留一次 df df.drop_duplicates(subset[song_id]) # 2. 类型转换把时长和热度强制转成数值转不出来的变成 NaN df[duration_ms] pd.to_numeric(df[duration_ms], errorscoerce) df[pop] pd.to_numeric(df[pop], errorscoerce) # 3. 补缺专辑为空补上占位符避免后续按专辑分组时报错 df[album] df[album].fillna(未知专辑) # 4. 异常截断正常歌曲时长在 30 秒到 10 分钟之间超出视为脏数据 df df[df[duration_ms].between(30000, 600000)] # 清洗后重置索引并生成分钟时长的派生列 df df.reset_index(dropTrue) df[duration_min] (df[duration_ms] / 60000).round(2)四步的顺序是有讲究的。先去重是因为后面所有统计都基于歌曲唯一性再转类型是因为fillna对字符串和数值的处理逻辑不同先转好类型再补缺更安全异常截断放在补缺之后是因为有些脏数据的duration_ms是空值转成 NaN 后between会直接过滤掉正好一并清出去。pop字段转成数值后如果接口里个别歌曲没有这个字段就会变成NaN。不建议直接删行可以看占比占比小就删占比大就用中位数填充。课程设计阶段常见做法是df[pop].fillna(df[pop].median(), inplaceTrue)这样不会把整首歌的数据丢掉。3.2 特征工程语种判定、热度分桶、歌手拆分清洗完只是拿到了干净的原数据距离能分析还差一层特征工程。歌手字段是 周杰伦/阿信 这种拼接格式没法直接统计热度是连续数值不分桶就看不出结构歌曲名能用来判定语种。这三个特征做完分析维度一下子就多了。import re # 语种判定按歌曲名里的字符类型粗略分类 def detect_language(name): if re.search(r[\u4e00-\u9fa5], str(name)): return 中文 if re.search(r[a-zA-Z], str(name)): return 欧美/日韩 return 纯音乐/其他 df[language] df[song_name].map(detect_language) # 热度分桶把 0-100 的热度值切成四档 bins [0, 30, 60, 80, 100] labels [冷门, 小众, 热门, 爆款] df[hot_level] pd.cut(df[pop], binsbins, labelslabels) # 歌手拆分把 A/B 拆成多行方便统计单人维度的分布 artist_series df[artist].str.split(/).explode().str.strip() artist_counts artist_series.value_counts()detect_language用的是正则粗判有中文就是中文歌没有中文但有英文字母就归到欧美/日韩两个都没有大概率是纯音乐。这个方法对课程设计来说足够但要注意日韩歌手的歌曲名经常是中文译名会被误判成中文。想更严谨可以用音译对照表但没必要答辩时讲清楚判定逻辑就能自圆其说。pd.cut是分桶的经典写法bins给区间端点labels给桶名。注意pd.cut的区间是左开右闭(30, 60] 落入小众刚好等于边界的值会被分到右侧桶里。artist_series用explode把一行里的多个歌手拆成独立行value_counts就能得到每个歌手的歌曲数这是后面做歌手排行榜的基础。3.3 清洗效果自检用一行代码确认数据可分析清洗和特征工程做完不要直接跳去画图先跑一遍自检。print(总歌曲数:, len(df)) print(去重后歌曲数:, df[song_id].nunique()) print(歌手数:, artist_series.nunique()) print(热度缺失:, df[pop].isna().sum()) print(df[hot_level].value_counts()) print(df.groupby(language)[song_name].count())这行代码的价值在于它能把清洗到底做了什么量化出来。比如df[song_id].nunique()小于len(df)说明还有重复级联去重没生效pop缺失数为 0说明中位数填充正常hot_level的四档数量分布合理说明分桶边界没设错。把这些输出截图放进实验报告比任何文字描述都有说服力这也是答辩时老师最爱看的中间产物。4. 可视化与分析模块从静态图表到交互大屏4.1 静态图表Matplotlib Seaborn 做歌单画像可视化是整个系统的脸面。先做静态图的原因很简单Matplotlib 出图稳定、答辩时方便贴进文档而且配置好了中文字体就基本不会翻车。下面这段代码生成四宫格一次性展示语种分布、热度分桶、时长分布和歌手 TOP 榜。import matplotlib.pyplot as plt import seaborn as sns # Windows 下常见中文字体二选一Linux 可换成 WenQuanYi plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False # 防止负号显示成方块 fig, axes plt.subplots(2, 2, figsize(14, 10)) df[language].value_counts().plot.pie( axaxes[0, 0], autopct%.1f%%, startangle90 ) axes[0, 0].set_title(语种分布) sns.countplot( datadf, xhot_level, order[冷门, 小众, 热门, 爆款], axaxes[0, 1] ) axes[0, 1].set_title(热度分桶) sns.histplot(df[duration_min], bins30, kdeTrue, axaxes[1, 0]) axes[1, 0].set_title(时长分布分钟) artist_counts.head(15).plot.barh(axaxes[1, 1]) axes[1, 1].set_title(歌手歌曲数 TOP15) plt.tight_layout() plt.savefig(playlist_analysis.png, dpi150)代码里的关键参数plt.rcParams[font.sans-serif]必须放在画图之前否则中文全部变成方块startangle90让饼图从正上方开始排布视觉上更整齐sns.countplot的order参数很重要因为热度分桶的标签是自定义的类别不指定顺序 Pandas 会按字母排成冷门/小众/热门/爆款的无序状态plt.savefig(..., dpi150)保存高清图论文和报告里插图够用。Seaborn 这里只用了countplot和histplot两个函数histplot的kdeTrue会叠加一条密度曲线能看出时长分布是否呈单峰形态——网易云歌单的时长分布一般集中在 3 到 5 分钟这个结论答辩时值得单独提。4.2 交互看板Pyecharts 组合图表静态图适合放文档但大作业演示环节用静态 Matplotlib 图会显得单薄。Pyecharts 是国产图表库生成的 HTML 页面可以直接在浏览器里打开图表自带悬浮提示、图例开关做强交互看板比 Matplotlib 方便得多。from pyecharts.charts import Bar, Pie from pyecharts import options as opts # 歌手分布柱状图 bar ( Bar() .add_xaxis(artist_counts.head(10).index.tolist()) .add_yaxis(歌曲数量, artist_counts.head(10).values.tolist()) .set_global_opts( title_optsopts.TitleOpts(title歌单歌手分布 TOP10), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate30)) ) ) bar.render(artist_bar.html) # 语种占比环形图同样数据用环形图展示更现代 pie ( Pie() .add( series_name语种, data_pair[(k, v) for k, v in df[language].value_counts().items()], radius[40%, 70%], # 内径 40% 外径 70%形成环形 ) .set_global_opts(title_optsopts.TitleOpts(title歌单语种构成)) ) pie.render(language_pie.html)Pyecharts 的链式调用是它的风格add_xaxis和add_yaxis分别传入 X 轴类别和 Y 轴数值。rotate30让 X 轴标签旋转 30 度避免歌手名过长时重叠。环形图的关键是radius[40%, 70%]百分比相对画布尺寸内径留空就是环形想做成实心饼图把内径改成0%即可。做完这两个基础图还可以把词云、雷达图、地图加进去。歌单分析系统最常见的组合是语种饼图 歌手柱状图 热度饼图 时长达标率仪表盘。展示时把这几个 HTML 文件放到同一个目录下写一个简单的index.html用 iframe 嵌到一起就是很标准的可视化大屏效果足够满足大作业的展示要求。4.3 挑四个最有答辩价值的分析维度图表做得多不等于分析到位。我从课程设计答辩经验里总结出四个最容易讲出深度的维度第一个是语种分布。能引出的结论是这个歌单的听歌偏好是否单一如果中文歌占比超过 80%说明歌单主理人有明确的华语偏好如果语种分布均匀说明是杂食型歌单。第二个是热度分桶。网易云热度值 0-100冷门歌曲占比高说明歌单在挖宝爆款占比高说明歌单偏大众向。把热门歌单独列出来还能看出歌单的定位是经典回顾还是新歌速递。第三个是歌手集中度。用artist_counts.head(10)占总数比例来算如果前 5 名歌手占了 50% 以上说明歌单是某歌手的粉丝向合集分布均匀则说明是主题歌单比如跑步节奏这种按场景收集的歌单。第四个是时长规律。计算平均时长、标准差、最长的歌是哪首。歌单里如果有大量 6 分钟以上的歌曲主题大概率是深度向如果集中在 3 分钟以下很可能偏快节奏。这个维度和歌单标题做交叉验证答辩效果极佳。5. 避坑指南网易云歌单分析系统最常见的 5 个翻车现场5.1 接口突然 403带上 Cookie 和 Referer 再试现象代码上午还能跑下午请求接口返回 403或者返回{code: -460}这类错误码。原因网易云对无登录态的匿名请求做了风控同一个 IP 短时间请求次数过多或者请求头缺少关键字段就会直接拒绝。-460通常表示参数签名错误或请求被风控拦截。解决请求头里补全User-Agent、Referer、Cookie三个字段。Cookie从浏览器开发者工具里复制登录网易云音乐网页版后在 Network 面板任意请求的 Headers 里找到Cookie整段复制。另外把每次请求间隔加到 1 秒以上不要用循环无脑刷。采集歌单 ID 列表时建议先睡 0.5 秒再发下一次请求。注意Cookie属于个人登录凭证不要写进提交的源码里。代码里用cookie_str os.getenv(MUSIC_COOKIE)从环境变量读取文档说明里注明需自行填入登录 Cookie避免隐私泄露。5.2 tracks 字段取不全分页与重试现象歌单明明有 500 首歌playlist_to_dataframe只解析出 100 多首或者df.info()显示song_id有大量空值。原因api/v6/playlist/detail接口对超大歌单有保护机制部分情况下只返回前若干首。另外歌单里混入已下架歌曲时tracks数组里会出现字段不完整的对象t.get(id)返回None。解决第一采集后用song_id.isna().sum()检查空值把空值行过滤掉第二如果歌单确实很大改用歌单接口的trackIds字段先拿到全部歌曲 ID再逐首调歌曲详情接口补全信息。课程设计阶段建议直接选 100 首左右的歌单数据量够分析又不会触发保护机制是最省事的做法。5.3 图表中文变方块字体配置要分清系统现象Matplotlib 画出的图里所有中文都显示成空心方块但英文和数字正常。原因Matplotlib 默认字体是 DejaVu Sans不支持中文。Windows 上最常见的配置是SimHei但如果你把代码原样搬到 macOSSimHei不存在又会变方块。解决用系统检测的方式配置字体而不是写死一个字体名。import matplotlib.pyplot as plt import platform system platform.system() if system Windows: plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] elif system Darwin: plt.rcParams[font.sans-serif] [PingFang SC, Hiragino Sans GB] else: plt.rcParams[font.sans-serif] [WenQuanYi Micro Hei, Noto Sans CJK SC] plt.rcParams[axes.unicode_minus] False这段配置放在所有画图代码之前。axes.unicode_minus也必须设成False否则坐标轴上的负号会显示成方块。答辩时如果用的是机房电脑Windows 字体名大概率是 SimHei这套配置能覆盖绝大多数环境。5.4 Pyecharts 渲染空白页面本地化 ECharts 资源现象bar.render(artist_bar.html)执行成功但浏览器打开 HTML 文件后一片空白开发者工具报Failed to load resource。原因Pyecharts 生成的 HTML 默认通过 CDN 加载 ECharts 的 JS 文件。答辩现场没有外网或者外网加载被防火墙拦了ECharts 脚本加载失败页面自然什么都不显示。解决在bar.render()之后检查 HTML 文件里的script src指向。如果是https://cdn.jsdelivr.net/...这类外链就把整个 HTML 文件离线保存前先确保你本机有 ECharts 的本地文件。最稳妥的方案是不依赖 Pyecharts 的 CDN直接用渲染后的 HTML 再把嵌入的 JS 下载放到本地目录但这种操作繁琐且容易引出版本问题。更常用的做法是答辩前把所有render()生成的 HTML 文件打开一遍确认每张图都能显示然后把图表截图保存为 PNG 作为兜底万一现场网络出问题直接展示截图不要现场等页面加载。提示Pyecharts 页面在部分旧版浏览器里渲染异常建议演示前确认机房浏览器是 Chrome 或 Edge 较新版本。用 iframe 嵌多个图表时每个 iframe 会独立加载一次 ECharts页面多时会卡控制在 4 个图表以内。5.5 高频爬取被限流频率控制与合规边界现象连续采集十几个歌单后请求开始随机超时甚至 IP 被临时封禁浏览器里正常访问网易云也变慢。原因短时间高频请求触发了服务端限流。网易云的接口风控会动态调整阈值同一 IP 的请求频率超过正常用户行为就会被限制。解决把采集频率控制在每 2 秒一个请求以内歌单数量控制在 20 个以内。代码里用time.sleep(1.5)做节流并且把采集过程封装成fetch_all_playlists(playlist_ids, interval1.5)间隔参数单独提出来方便调整。这里必须强调一个边界这个分析系统的用途是课程学习和研究歌单的元数据歌名、歌手、时长、热度不要拿它去批量抓取歌曲评论、用户信息更不要抓取音频资源本身做二次分发。文档说明里要明确写清仅用于学习与学术用途这是大作业的基本合规要求。6. 从能跑到能答辩系统架构、文档说明与演示技巧6.1 三层架构怎么在答辩里讲清楚代码跑通只是完成了 50%剩下 50% 在于你能不能把系统的设计思路讲明白。我建议在文档说明里把系统拆成三层采集层、分析层、展示层。采集层对应fetcher.py职责是请求接口、解析 JSON、落库分析层对应analyzer.py职责是数据清洗、特征工程、统计计算展示层对应visualizer.py职责是生成 Matplotlib 静态图和 Pyecharts 交互页面。每层之间只通过music.db传递数据不互相调用函数。答辩时按采集层拿到原始数据 → 分析层清洗加工 → 展示层输出结论这条链路讲逻辑清晰老师一听就明白这是完整的分层设计而不是把所有代码堆在一个脚本里。6.2 文档说明的套路README 和实验报告各写什么源码包里的文档说明一般包含两份一份是 README给运行代码的人看一份是实验报告给评分的老师看。README 写三块就够环境依赖Python 版本、pip install -r requirements.txt、运行步骤先配 Cookie 环境变量再按顺序执行三个脚本、目录结构每个文件一句话说明。实验报告要写出过程感需求分析、数据来源说明、清洗前后的数据量对比、四张分析图表的结论、遇到的问题与解决方式。这里有个容易踩的坑文档里不要贴大段代码而是贴清洗前后的对比数据和图表截图。老师看报告时间有限数据对比和可视化结果比代码更能证明工作量。实验报告里附上df.info()的输出截图、去重前后行数对比、hot_level分桶统计表这比任何文字都有说服力。6.3 演示前的自检清单最后整理一份自检清单这是我带课程设计项目时的血泪经验每一条都对应真实翻车现场检查项操作常见问题数据库连接确认music.db与脚本同目录路径写错导致查不到数据中文字体运行一张含中文的测试图机房电脑缺字体中文变方块Pyecharts 页面逐个打开所有 HTML外网受限时页面空白需准备截图兜底请求频率重新采集时确认 sleep 生效频率过高导致接口 403答辩电脑分辨率检查图表在投影上是否清晰字号过小后排看不清坐标轴标签我的习惯是答辩前一天跑完整流程三遍第一遍清空数据库从零采集第二遍只做分析和可视化第三遍只打开 HTML 确认展示效果。三遍都过了才敢把项目拷进 U 盘。这套流程走完后你会发现所谓稳健的系统其实就是把每个环节的失败路径提前堵死剩下的交给临场发挥就行。希望帮到你。本文还有配套的精品资源点击获取
返回列表