ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

网易云音乐歌单分析:Python爬虫、数据清洗与可视化全流程

2026/9/15 3:14:29 拓冰建站 浏览量
网易云音乐歌单分析:Python爬虫、数据清洗与可视化全流程 简介一份面向Python数据分析初学者的网易云音乐歌单分析系统完整源码源自高分期末大作业覆盖数据获取、清洗、可视化全流程。项目基于requests、numpy、pandas、matplotlib、squarify、jieba、wordcloud等模块通过爬虫采集歌单数据并对其评论、收藏、播放、贡献分布等进行可视化同时利用词云展示关键词给出歌单优化建议。压缩包共38个文件包含12个py源文件、11个pyc缓存、7张png结果图、3个csv数据文件、以及pdf、docx、jpg、md等文档资料整体大小12.23MB目录结构清楚便于按模块研读。目前已有785人学习下载。资料内含完整代码、数据采集与分析脚本、可视化输出图件和项目说明文档适合课程设计、期末作业参考也可作为数据可视化入门练手素材。1. 从歌单 ID 到播放量曲线网易云音乐歌单分析系统的实际工作量一门 Python 数据分析课教完 pandas 和 Matplotlib期末作业里十有八九是超市销售或电商订单的 CSV 分析。数据是现成的答案也是固定的做完除了截图没有留下任何自己的东西。网易云音乐歌单分析不一样数据要自己抓字段要自己清洗结论要自己定义正好把 Python 爬虫、数据分析、数据可视化串成一条完整的线。它回答的问题也很具体——哪些标签的歌单更容易冲高播放量、歌单创建时间分布在什么时段、收藏数和播放量之间有没有相关性。下面这套方案按「接口请求 → 数据清洗 → 可视化出图 → 可复现交付」的顺序展开适合作为期末大作业的落地路径也适合想拿真实数据练一遍完整流程的人参考。2. 网易云歌单数据抓取请求头、搜索接口与 Cookie 策略2.1 为什么选 Web API 而不是 Selenium 模拟点击做网易云音乐歌单分析第一步是拿数据。常见的做法是直接请求网易云音乐 Web 端本身的 JSON 接口而不是用 Selenium 模拟浏览器点击。原因有两个速度和风控。搜索接口一次返回 50 条歌单一个关键词翻几页就能得到几百条十分钟之内完成Selenium 要打开页面、等待渲染、滚动加载同样数据量耗时半小时起步。风控方面Selenium 的浏览器特征非常明显验证码出现的概率高而普通 requests 请求把 User-Agent 和 Referer 配齐后行为接近自然流量反而不容易触发限制。2.2 先跑通最小请求歌单详情接口不管从哪个入口拿到的歌单 ID最终都要落到歌单详情接口上取完整字段。最小可用的请求是这样的import requests def get_playlist_detail(playlist_id: int) - dict: url https://music.163.com/api/v6/playlist/detail headers { User-Agent: ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 ), Referer: https://music.163.com/, } resp requests.get(url, params{id: playlist_id}, headersheaders, timeout10) resp.raise_for_status() return resp.json()逻辑说明参数id是歌单的唯一标识响应 JSON 里的playlist字段包含名称、播放量、收藏数、标签、创建时间和歌曲列表。请求头必须带Referer否则服务端会拒绝响应timeout10防止单次请求卡死整个抓取流程raise_for_status()会把 4xx 和 5xx 状态码直接变成异常抛出批量抓取时才能准确定位是哪个 ID 出了问题。2.3 搜索接口的请求参数与批量翻页歌单详情接口需要 IDID 从哪里来搜索接口是主要入口按关键词把歌单列表拉回来。关键参数比详情接口多两个type和offset。def search_playlists(keyword: str, page: int 0, limit: int 50) - list: url https://music.163.com/api/search/get/web params { s: keyword, type: 1000, # 1000 表示搜索对象是歌单 limit: limit, # 单页条数最大 100 offset: page * limit, } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://music.163.com/, } resp requests.get(url, paramsparams, headersheaders, timeout10) data resp.json() return data[result][playlists]参数说明type1000表示资源类型是歌单改成1搜出来的是单曲改成100是歌手抄代码前先确认这一个值。limit建议设 50设 100 响应时间明显变长且容易被限流。offset等于页码 * limit用于翻页。歌单搜索结果大约只能翻到 50 页超过之后返回空列表这是服务端限制不是代码逻辑错误。这些参数答辩时常被老师追问下面的参数表可以直接写进文档说明参数取值作用s关键词如「华语」「深夜 emo」定义歌单集合的语义范围type1000搜索资源类型1014 是视频、1000 是歌单limit10100单页返回条数建议 50offset页码 × limit翻页偏移与 limit 配合遍历2.4 Cookie、限速与异常重试不带 Cookie 直接请求也能跑通但连续拉几百条之后会遇到 403。常见处理是打开浏览器访问 music.163.com登录后在开发者工具里复制请求头中的 Cookie 字段加到 headers 里。匿名状态下服务端下发的 NMTID 标记也要带上有了它请求行为更接近正常用户。注意不要用登录后的高权限 Cookie 做长时间大量抓取风险等级完全不同。批量抓取必须限速每 0.3 秒一次是底线再辅以异常重试import time results [] for page in range(5): for _ in range(3): # 最多重试 3 次 try: items search_playlists(华语, pagepage) results.extend(items) break except Exception: time.sleep(1) time.sleep(0.3)逻辑说明内层循环负责重试单次失败先等 1 秒再试连续 3 次失败就放弃这一页外层time.sleep(0.3)是正常的请求间隔把请求频率压到每秒 3 条以内。真实项目里抓到原始 JSON 后先落盘保存不要直接进 DataFrame清洗阶段改逻辑时不用重新请求接口也避免答辩现场断网后无数据可用。实在抓不动时找一份公开的网易云歌单数据集 CSV 作为替代输入课程设计完全够用。提示Cookie 有有效期课程设计通常当天写完当天跑完第二天如果突然 403第一反应应该是看 Cookie 是否过期而不是怀疑代码变了。3. 歌单数据清洗把嵌套 JSON 拍平成 DataFrame3.1 字段提取与嵌套结构展开搜索接口返回的每条歌单是嵌套字典creator里套着昵称tags是字符串数组。清洗第一步是把需要的字段抽出来拍平成一行一条歌单的表格。import pandas as pd def build_dataframe(playlists: list) - pd.DataFrame: rows [] for item in playlists: rows.append({ playlist_id: item.get(id), name: item.get(name), play_count: item.get(playCount), book_count: item.get(bookCount), track_count: item.get(trackCount), create_time: item.get(createTime), tags: ,.join(item.get(tags) or []), creator: (item.get(creator) or {}).get(nickname, ), }) return pd.DataFrame(rows)逻辑说明item.get(tags) or []是为了处理标签为空的歌单避免join一个None报错(item.get(creator) or {}).get(nickname, )同理个别歌单没有创作者信息时直接给空字符串保证所有行的字段数量一致。tags用逗号拼成字符串后面做词频统计时一次split就能拆开比存数组更省事。3.2 毫秒时间戳转年份与创建时段createTime是毫秒级时间戳原始值是一串 13 位数字直接画图没有意义。数据分析阶段通常只需要年份和创建的小时转换如下df[create_time] pd.to_datetime( df[create_time], unitms, utcTrue ).dt.tz_convert(Asia/Shanghai) df[year] df[create_time].dt.year df[hour] df[create_time].dt.hour参数说明unitms声明时间戳单位是毫秒漏掉这个参数时间会被解释成纳秒得到的年份完全错误。utcTrue配合tz_convert(Asia/Shanghai)是把时间统一到中国时区不做这一步晚上创建的歌单会被算到次日按小时统计时段分布时就偏了。注意如果把create_time直接转成字符串再保存 CSV读回来时会变成 object 类型再次做dt.year会报 AttributeError。要么保存时用转换之后的值要么读回来重新转一次。3.3 清洗后的数据字典与缺失值处理清洗完成后的 DataFrame 结构如下这份数据字典可以直接复制到文档说明里列名类型示例说明playlist_idint3778678歌单唯一 IDnamestr私人雷达歌单名称play_countint1287453累计播放量book_countint32098收藏数track_countint50歌曲数量tagsstr华语流行逗号分隔的标签create_timedatetime642023-05-01 22:14:0008:00创建时间yearint2023创建年份hourint22创建的小时清洗这一层有两个高频坑。第一个是重复数据翻页时如果 offset 计算错误同一首歌单可能被抓两次。处理方式是df.drop_duplicates(subset[playlist_id])只按 ID 去重不要用歌单名称因为不同用户会创建同名歌单。第二个是数值字段类型漂移如果经过 CSV 落盘再读回play_count可能变成字符串后续做加减和排序都会异常。保险做法是读 CSV 时显式声明dtype{play_count: int}或者干脆存成 parquet 格式保留类型信息。4. 数据可视化实现网易云歌单的播放量分布、标签词频与时间趋势4.1 播放量分布对数坐标直方图歌单播放量从几百到几千万跨度五个数量级。直接画直方图小播放量的歌单会全部挤在最左侧一根柱子上看不出分布形状。先取对数再画图是标准做法import matplotlib.pyplot as plt import numpy as np plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots(figsize(8, 5)) ax.hist(np.log10(df[play_count]), bins30, color#2d8cf0, alpha0.8) ax.set_xlabel(log10(播放量)) ax.set_ylabel(歌单数量) ax.set_title(歌单播放量分布对数坐标) plt.tight_layout() plt.savefig(output/play_count_dist.png, dpi150)逻辑说明np.log10把播放量映射到对数空间横轴刻度 3、4、5、6 分别对应一千、一万、十万、一百万播放。bins30控制分箱数量抓回来的歌单超过 500 条时可以把 bins 加到 50看更细的双峰或多峰结构。如果看到分布明显右偏且集中在低播放量段说明抓取的关键词偏向小众歌单换一个更大众的关键词重新抓再对比。4.2 歌单标签词频从逗号分隔到水平条形图标签字段在清洗阶段已经拼成了逗号分隔字符串。统计词频只需两步拆开、计数。用Counter比手动循环快结果也更直观from collections import Counter tag_counter Counter() for tags in df[tags].dropna(): for tag in tags.split(,): tag tag.strip() if tag: tag_counter[tag] 1 top_tags tag_counter.most_common(15) names [t[0] for t in top_tags][::-1] counts [t[1] for t in top_tags][::-1] plt.figure(figsize(8, 6)) plt.barh(names, counts, color#19be6b) plt.xlabel(出现次数) plt.title(歌单标签 Top 15) plt.tight_layout() plt.savefig(output/tag_top15.png, dpi150)参数说明dropna()先丢掉标签为空的记录strip()去掉拆出来的首尾空格避免同一个标签因为空格不同被算成两个词。[::-1]反转列表让出现次数最多的标签显示在最上方水平条形图这样做阅读体验最好。如果 Top 15 里有「华语」「流行」这类大词占满榜单可以改成tag_counter.most_common(50)后手动排除前几个大词再看细分标签的分布。4.3 交互式视图用 pyecharts 生成可缩放 HTMLMatplotlib 的静态图压缩了信息密度答辩投屏时老师想悬停看具体数值做不到。pyecharts 生成的是独立 HTML 文件双击就能在浏览器打开缩放、悬浮提示都有而且不需要 Jupyter 环境。代码上比 Matplotlib 更简洁from pyecharts.charts import Bar from pyecharts import options as opts bar ( Bar() .add_xaxis([t[0] for t in top_tags][::-1]) .add_yaxis(歌单数量, [t[1] for t in top_tags][::-1]) .set_global_opts( title_optsopts.TitleOpts(title网易云歌单标签分布), xaxis_optsopts.AxisOpts(rotate30), ) ) bar.render(output/tag_top15.html)pyecharts 的默认配置自带中文字体不需要像 Matplotlib 那样处理乱码。rotate30是 x 轴标签旋转角度标签超过六个字时建议打开。三张图分别 render 之后再用Tab组件把它们拼进同一个 HTML就有点数据可视化大屏的意思答辩时在一个页面里切图比来回开关文件顺畅得多。4.4 可视化阶段的三个高频报错与处理课程设计里可视化报错集中在字体、刻度和渲染三类对照表可以直接进文档说明报错现象原因处理方式Font family [SimHei] not foundLinux 服务器上没有 SimHei 字体执行fc-list :langzh查看系统可用中文字体把字体名填进font.sans-serifx 轴标签叠成黑色一团类目文本过长且未旋转加plt.xticks(rotation45)或只保留 Top 10 减少条数pyecharts HTML 打开是空白输出文件名含中文或空格文件名改成纯英文加下划线如tag_top15.html第一行对应服务端或同学电脑上跑 Matplotlib 的典型场景font.sans-serif设置成列表是为了让系统按顺序回退查找。第二行在歌单名称做横轴时尤其常见纯粹是绘图宽度不够的视觉问题不是数据问题。第三行是 pyecharts 的render内部逻辑对非 ASCII 路径兼容不好导致的改文件名最省事。5. 期末答辩验收技巧把歌单分析流程参数化成一条命令期末大作业最容易被追问的一句话是「换个关键词还能跑吗」。如果整套分析是改一个变量跑一次的写法现场演示时老师指定一个新词你只能在代码里找s 华语这一行改掉再从头运行中间任何一步报错都会很被动。更好的做法是把流程拆成抓取和分析两个脚本中间用 CSV 衔接命令行参数控制关键词和路径。# analyze.py import argparse import pandas as pd parser argparse.ArgumentParser(description歌单数据分析入口) parser.add_argument(--input, defaultplaylists.csv, help抓取阶段输出的 CSV 路径) parser.add_argument(--topn, typeint, default15, help标签 Top N 的条数) args parser.parse_args() df pd.read_csv(args.input, dtype{playlist_id: int}) print(df.groupby(year).size().sort_index(ascendingFalse))抓取脚本负责把原始结果清洗后存成playlists.csv分析脚本只读 CSV。演示时先跑python fetch.py --keyword 华语 --pages 3再跑python analyze.py --input 华语.csv --topn 20换关键词只需要改--keyword一个参数。文档说明里把「第一个脚本负责数据采集与清洗第二个脚本负责分析与可视化」写清楚这就是一个完整的数据分析项目结构而不是几个散落的脚本。再补一个细节df.to_csv(clean.csv, indexFalse)生成的文件用 Excel 打开时中文会乱码因为 pandas 默认输出无 BOM 的 UTF-8。在to_csv里加encodingutf-8-sigExcel 就能正确识别中文列名。答辩前把两张 PNG 和 HTML 统一放进output/目录文档说明里附目录树。现场演示就按这个顺序走先执行抓取脚本再执行分析脚本最后打开output/tag_top15.html交互页面鼠标悬停在柱子上说数据。本文还有配套的精品资源点击获取