ARTICLE DETAIL

资讯详情

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

Python数据分析实战:从网易云歌单采集到可视化的完整流程

Python数据分析实战:从网易云歌单采集到可视化的完整流程 简介基于Python数据可视化的网易云音乐歌单分析系统源码与文档面向Python数据分析初学者以及需要完成期末大作业、课程设计的高校学生。项目以网易云音乐歌单为分析对象利用requests和bs4爬取歌单名称、评论、收藏、播放等数据再通过pandas进行缺失值处理、格式转换等清洗工作结合matplotlib、squarify等库绘制评论数、收藏数、播放数、贡献度等维度图表并借助jieba分词与wordcloud生成关键词词云最终提出歌单优化建议形成完整的数据分析闭环。资源共38个文件包含12个Python脚本、3个CSV数据文件、7张PNG可视化结果图、1个中文字体文件同时附有PDF、Word版结课报告和说明文档压缩包大小12.23MB整体目录结构清晰、关键代码注释完整便于直接运行和二次修改。已有785人学习下载适合作为数据分析入门实践参考也可作为期末项目模板快速复用。1. Python 数据分析初探项目为什么拿它当期末大作业的人最不慌网易云音乐的歌单页面普通用户看到的是“每日推荐”“热歌榜”做数据分析的人看到的是另一层东西播放量、收藏数、标签分布、创建者信息这些字段天然适合做一次完整的数据采集、清洗、聚合、可视化演练。这个“Python数据分析初探项目 基于Python数据可视化的网易云音乐歌单分析系统源码文档说明高分期末大作业”本质上就是用 Python 把歌单数据变成图表再用一个网页或看板把结论展示出来。做课程设计、期末大作业、甚至是简历上的第一个数据项目它都够用——数据容易拿、字段够丰富、可视化效果直观、答辩时讲得清楚。适合正在学 Python 数据分析但还没做过完整项目的同学也适合想在一周内交付一个“能跑、能说、有图”的作业的从业者。下面按我实际做这类项目的顺序把从爬虫到可视化的每一条路都走一遍。2. 先搞懂歌单数据集分析对象里有哪几类能用的字段2.1 歌单的字段不只是歌名播放量、标签、创建者才是分析重点网易云歌单接口返回的数据比你在网页上看到的多得多。常见做法是请求https://music.163.com/api/playlist/list/hot这个接口它返回的每一条歌单记录里至少包含这些字段id歌单唯一编号去重和后续详情查询都靠它。name歌单标题比如“深夜emo自救指南”。playCount播放次数这是整个项目里最核心的数值指标。trackCount歌曲数量能看出歌单是“精选 20 首”还是“超大杯 500 首”。tags标签数组比如“华语”“治愈”“通勤”做标签热度统计全靠它。creator创建者对象里面还有nickname和level可以做“哪些人更爱建歌单”的分析。updateTime更新时间的时间戳通常是毫秒级可以用来分析歌单更新频率。我第一次做的时候犯了个错只把name和playCount存下来就急匆匆去画图结果发现图只有一根柱子毫无可讲的东西。做数据分析项目数据维度要先铺开再做减法。建议你把能拿到的字段全部落库哪怕暂时用不上后面做交叉分析时少跑一趟采集。2.2 用 requests 把歌单列表抓下来最小可运行代码采集这块我一般直接在 Python 脚本里用requests写一个函数按页拉取。注意接口的cat参数可以指定歌单分类order可以按热度或最新排序。为了让作业图表更丰富我通常拉 4 到 5 个分类每类 3 页左右。import requests import time import pandas as pd # 网易云歌单热歌榜接口公开可访问实际请求时需要带浏览器里的 Cookie URL https://music.163.com/api/playlist/list/hot headers { User-Agent: ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0 Safari/537.36 ), Referer: https://music.163.com/, # 建议登录网页版后从开发者工具里复制自己的 Cookie 填到这里 Cookie: 你自己的cookie } def fetch_playlists(cat全部, offset0, limit30): params { cat: cat, order: hot, # hot 表示按热度排序 limit: limit, # 单页数量接口上限一般是 30 offset: offset, # 分页偏移第二页就是 30 } resp requests.get(URL, paramsparams, headersheaders, timeout10) resp.raise_for_status() data resp.json() return data.get(playlists, []) rows [] for cat in [华语, 流行, 治愈, 电子]: for page in range(3): playlists fetch_playlists(catcat, offsetpage * 30) for pl in playlists: creator pl.get(creator, {}) or {} rows.append({ 歌单ID: pl.get(id), 歌单名: pl.get(name), 播放量: pl.get(playCount), 歌曲数: pl.get(trackCount), 标签: ,.join(pl.get(tags, []) or []), 创建者: creator.get(nickname), 创建者等级: creator.get(level), 更新时间戳: pl.get(updateTime), }) time.sleep(1) # 控制请求频率避免被风控 df pd.DataFrame(rows) df.to_csv(playlists.csv, indexFalse, encodingutf-8-sig) print(f共采集 {df.shape[0]} 条歌单)代码逻辑很简单两层循环外层遍历分类内层翻页每次把歌单字段抽出来存成字典最后统一转成 DataFrame。这里有两处需要你手动调整Cookie必须换成你自己登录后的值否则接口可能直接返回{code: 401}time.sleep(1)不要删从 0.5 秒到 2 秒都可以但别把频率拉满否则封 IP 是玄学但封了就很麻烦。2.3 选存储方案CSV 适合交作业SQLite 适合想多问几个问题的读者课程设计阶段CSV 是最省事的。to_csv一行代码搞定pandas 读回来也方便答辩时还能用 Excel 打开给老师看。但如果你想让项目显得更“系统”有个常见做法是把数据存进 SQLite。import sqlite3 conn sqlite3.connect(playlists.db) df.to_sql(playlists, conn, if_existsreplace, indexFalse) conn.close()好处是后续可以用 SQL 做聚合比如“播放量前 10 的歌单里标签分布是什么”。对于期末大作业来说SQLite 文件只需要多写三行代码但文档里能多写一小节“数据存储设计”性价比挺高。不过不建议你在这个阶段上 MySQL老师大概率不会开服务器让你连数据库本地 SQLite 足够。3. 数据清洗与聚合把原始字段变成能画图的指标3.1 清洗脏数据播放量里的空值和单位是第一个坑接口返回的playCount正常情况下是整数但你拿到的数据里总会有意外有的歌单没有创建者信息creator是空字典有的tags是 null更常见的是你把数据存成 CSV 再读回来时数值列被 pandas 推断成float里面混了 NaN。先做一步标准清洗import pandas as pd df pd.read_csv(playlists.csv, encodingutf-8-sig) # 播放量转成数值空值直接丢弃 df[播放量] pd.to_numeric(df[播放量], errorscoerce) df df.dropna(subset[播放量]) # 标签为空的行填成未分类 df[标签] df[标签].fillna(未分类) # 更新时间戳转换为日期格式毫秒要除以 1000 df[更新时间] pd.to_datetime(df[更新时间戳], unitms, errorscoerce)errorscoerce是把非法值变成 NaN而不是抛异常这在处理不干净的数据时是“后悔药”一样的存在。转换后立即dropna保证后面所有聚合操作不会因为空值报错。时间戳那个地方最容易翻车网易云的updateTime是毫秒直接pd.to_datetime(df[更新时间戳])会把 2024 年变成 1970 年附近的诡异时间所以一定要带unitms。3.2 用 pandas 做聚合标签热度和播放量分布怎么算清洗完之后先做两个最基础也最好讲的统计标签热度 Top10 和播放量分布区间。# 标签是一行里用逗号拼接的字符串拆开成多行再统计 tags df[标签].str.split(,).explode().str.strip() tag_count tags.value_counts().head(10).to_frame(歌单数) tag_count[占比] (tag_count[歌单数] / len(df) * 100).round(2) print(tag_count)explode()是处理“一个单元格里塞了多个值”的标准手段把“华语,治愈”拆成两行再计数。这里有个细节拆出来的标签可能带前后空格所以要先str.strip()否则“华语”和“华语 ”会被当成两个标签。再看播放量分布我一般用pd.cut做分箱bins [0, 10000, 50000, 100000, 500000, float(inf)] labels [1万以下, 1-5万, 5-10万, 10-50万, 50万以上] df[播放量区间] pd.cut(df[播放量], binsbins, labelslabels, rightFalse) distribution df[播放量区间].value_counts().sort_index() print(distribution)分箱的意义在于单看“平均播放量”没有意义因为头部歌单和长尾歌单差了好几个数量级。把区间列出来老师一眼就能看到“歌单播放量呈明显长尾分布”这句话本身就是答辩时的分析结论。3.3 造“可解释”指标怎样让一个歌单的“热度”能被横向比较只拿播放量排序图是能画但显得太直白。我习惯造一个综合热度分把播放量、歌曲数、创建者等级三个维度压成一个 0-100 的分数。这一步能让你的项目从“画图”升级成“定义指标”。# 先对播放量做 log 变换压缩极值影响 import numpy as np df[播放量log] np.log1p(df[播放量]) # 三个分维度做 Min-Max 归一化 def minmax(series): return (series - series.min()) / (series.max() - series.min()) df[播放量得分] minmax(df[播放量log]) df[歌曲数得分] minmax(df[歌曲数].fillna(0)) df[创建者等级得分] minmax(df[创建者等级].fillna(0)) # 加权求和播放量权重最大歌曲数其次创建者等级权重最低 df[热度分] ( df[播放量得分] * 0.5 df[歌曲数得分] * 0.3 df[创建者等级得分] * 0.2 ) df[热度分] (df[热度分] * 100).round(2)注意创建者等级可能缺失所以先fillna(0)再做归一化不然整列都是 NaN。log1p是对数变换防止“播放量一亿的歌单把其他歌单全压扁”。三个权重加起来等于 1答辩时问你权重怎么设的你就说“播放量最能反映歌单受欢迎程度所以给 0.5歌曲数反映内容丰富度给 0.3创建者等级是弱信号给 0.2”。这个口径说得通比拍脑袋设权重可信得多。4. 数据可视化用 ECharts 把歌单数据变成能答辩的网页看板4.1 课程设计选型Matplotlib 画不出“系统感”ECharts 才是正路数据分析与可视化方向的大作业画图工具不外乎 Matplotlib、Seaborn、ECharts。如果你只想做一张静态图Matplotlib 三行代码就能出图但问题在于期末大作业要的“系统”通常需要一个网页形式的看板老师打开浏览器就能看到交互效果。Matplotlib 画完导出 PNG 再贴进 Word 里视觉上输了一半。ECharts 的好处有三个图表类型多柱状图、饼图、词云都有现成组件、交互是原生的鼠标悬停显示数值、和 Flask 搭配起来像模像样。下面用 Flask 做接口前端用 ECharts 渲染形成一个完整的小系统。4.2 Flask 数据接口把 pandas 的分析结果通过 API 吐给前端后端的职责是读 CSV 或 SQLite算出指标然后用jsonify返回给浏览器。以“播放量 Top10”和“标签热度 Top10”两个图为例Flask 代码这样写from flask import Flask, jsonify, render_template import pandas as pd app Flask(__name__) def load_data(): df pd.read_csv(playlists.csv, encodingutf-8-sig) df[播放量] pd.to_numeric(df[播放量], errorscoerce) return df.dropna(subset[播放量]) app.route(/) def index(): return render_template(index.html) app.route(/api/top_playlists) def top_playlists(): df load_data() top10 df.nlargest(10, 播放量) return jsonify({ names: top10[歌单名].tolist(), plays: top10[播放量].astype(int).tolist(), }) app.route(/api/top_tags) def top_tags(): df load_data() tags df[标签].fillna(未分类).str.split(,).explode().str.strip() top_tags tags.value_counts().head(10) return jsonify({ tags: top_tags.index.tolist(), counts: top_tags.values.tolist(), }) if __name__ __main__: app.run(debugTrue)df.nlargest(10, 播放量)比先排序再head(10)更直观专门用来取前 N 条。接口返回的 JSON 结构刻意设计成“两个数组”前端拿到后直接塞进 ECharts 的xAxis.data和series.data少一层转换。实际开发时load_data()每次都全量读 CSV数据量小没问题但答辩时如果老师问性能你要能答出“可以改成启动时加载一次”这个优化点。4.3 前端页面一个 HTML 模板同时渲染柱状图和饼图在templates/index.html里写两个图表容器。注意 ECharts 的 JS 文件下载到本地static目录不要在答辩时依赖外网 CDN否则教室 WiFi 一断图表全白。!DOCTYPE html html langzh-CN head meta charsetUTF-8 title网易云音乐歌单分析系统/title script src/static/echarts.min.js/script /head body div idbar styleheight: 400px;/div div idpie styleheight: 400px;/div script async function draw() { const barResp await fetch(/api/top_playlists); const barData await barResp.json(); const barChart echarts.init(document.getElementById(bar)); barChart.setOption({ title: { text: 播放量 Top10 歌单 }, xAxis: { type: category, data: barData.names }, yAxis: { type: value }, series: [{ type: bar, data: barData.plays }] }); const pieResp await fetch(/api/top_tags); const pieData await pieResp.json(); const pieChart echarts.init(document.getElementById(pie)); pieChart.setOption({ title: { text: 歌单标签热度分布 }, series: [{ type: pie, data: pieData.tags.map((name, i) ({ name: name, value: pieData.counts[i] })) }] }); } draw(); /script /body /htmlasync/await写法注意顺序两个fetch相互独立可以并发这里为了读代码方便写成串行。ECharts 的setOption是增量更新第一次调用就完成了初始化加渲染。关于中文标签务必确认 HTML 文件保存为 UTF-8 编码并在head里写了charsetUTF-8否则 Linux 服务器上大概率出现“方块字”。5. 避坑清单期末作业最容易翻车的 4 个细节5.1 现象requests 请求接口返回 400或者报{code: 401}原因网易云接口对未登录请求做校验缺失Cookie或User-Agent不符合浏览器特征时直接拒掉。刚开始我试过不带任何请求头裸请求返回的 JSON 永远是“账号未登录”。解决在浏览器里登录网页版网易云按 F12 打开开发者工具找到 Network 面板任选一个 XHR 请求把请求头里的Cookie完整复制进代码。同时把User-Agent也带上。注意 Cookie 有有效期隔几天再跑要重新复制这是做爬虫数据采集的血泪经验。5.2 现象pandas 读 CSV 后播放量列全是 float还有 NaN原因CSV 里某些行播放量缺值pandas 推断类型时会把整列变成float64因为 int 列装不下 NaN。同理创建者等级也会因为空值变成 float后续fillna(0)是必要操作。解决读取数据后统一用pd.to_numeric(..., errorscoerce)做一次强转再dropna或fillna。这样能保证后续nlargest、cut操作不报类型错误。另一个细节是to_csv时用encodingutf-8-sig而不是utf-8否则用 Excel 打开时中文会乱码——这个坑几乎每个做期末大作业的人都会踩一次。5.3 现象前端图表标题和标签显示为方块刷新也没用原因HTML 页面本身不是 UTF-8 编码。Windows 下用记事本默认另存为 ANSI 编码浏览器按 UTF-8 解码就炸了。还有一种情况是 ECharts 的echarts.min.js下载不完整静态资源 404控制台报错。解决写代码时用 VS Code 一类的编辑器右下角把文件编码切成 UTF-8。echarts.min.js不要用unpkg或jsdelivr在线地址下载到本地static目录。凡是用了本地静态资源检查路径Flask 默认把static目录放在项目根目录模板路径写/static/echarts.min.js如果报 404先看有没有拼错目录名。5.4 现象答辩时老师问“你的数据是真的吗”你支支吾吾原因图是画出来了但你说不清数据从哪来、样本量是多少、有没有清洗过。一是数据采集脚本没有留下“可复现”的证据二是分析口径没有写进文档。解决在文档里加一节“数据来源与采集约束”写明数据来自网易云公开接口采集时间为某年某月某日共采集 N 条歌单、覆盖 4 个分类、每条记录包含哪些字段再写明清洗规则去重、类型转换、空值处理。同时把采集脚本和 CSV 文件一起放进源码包老师想复现也能跑。有个额外的好处如果答辩现场网络断了你可以理直气壮说“数据已落盘分析不依赖在线接口”这反而是加分项。6. 做“高分期末大作业”的最后一公里把展示能力补到 80 分以上前面五章做完已经是一个完整的“数据采集-清洗-分析-可视化”闭环。但要让作业在班里冒头我通常会再补一个时间维度的小分析歌单播放量和更新时间的关系。因为平台的热门歌单通常不断被编辑更新一个 2024 年创建的歌单播放量破百万和一个 2016 年创建的歌单播放量破百万背后的信号完全不同。df[年份] df[更新时间].dt.year year_group df.groupby(年份)[播放量].agg([count, mean]).round(0) print(year_group)groupby之后同时求数量和均值用的是一次聚合方法。结论通常很典型老歌单数量少但均播放量高新歌单数量多但均值被长尾拉低。这个结论放进答辩 PPT比单纯截图两张 ECharts 图有说服力得多。答辩演示时有个顺序建议先讲“我定义了综合热度分因为播放量单指标有偏”再打开网页看板用 Top10 柱状图引出长尾分布用标签饼图引出“华语、治愈、通勤”这些高频标签。每个图表只讲一个核心结论讲完就停不要堆形容词。老师问“为什么权重这么设”就把 0.5/0.3/0.2 的口径再说一遍。这是我能给的最实用的答辩话术。我自己的习惯是项目完成后把采集脚本、清洗脚本、Flask 代码、前端模板、README 说明文档放在同一个目录用pip freeze requirements.txt冻结依赖版本。这么做的好处是哪怕三个月后要重新跑也不会因为 pandas 版本升级导致 API 变化。希望这次整理的从采集到可视化的完整路径能帮到你按照这个顺序做下来你的期末大作业就不会在最后一步卡壳。本文还有配套的精品资源点击获取
返回列表