ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于Python的网易云音乐数据分析系统设计与实现

基于Python的网易云音乐数据分析系统设计与实现 1. 项目概述与选题价值每年到了毕业设计季总有学弟学妹来问我同一个问题我想做个Python数据分析相关的题目但不知道选什么方向太简单的怕过不了太复杂的又怕做不完有没有那种既不太难、又能讲出东西来的题目如果你也正处于这个阶段那基于Python的网易云歌曲数据分析系统这个题目我是真心推荐你考虑一下。它的核心思路很清晰用爬虫采集网易云音乐排行榜数据经过清洗和存储后从多个维度做统计分析最后通过可视化图表把结果展示出来。听起来很常规对吧但它实际覆盖了Python数据分析岗所需的核心技能链——爬虫采集、数据处理、存储设计、分析建模、可视化展示整套流程走下来你的简历上就能写上一句独立完成过完整的数据分析项目。这个题目的另一个好处是数据源足够丰富。网易云音乐排行榜包含飙升榜、新歌榜、原创榜、热歌榜等多个榜单每个榜单的歌曲信息、歌手信息、评论数据都能抓取下来做分析。你想研究歌手热度变化也好分析歌曲风格趋势也好还是想看看评论情感倾向都有足够的素材可以发挥。对比那些用静态CSV文件做分析的题目这种有真实爬虫过程的项目明显更有说服力答辩的时候老师问起数据怎么来的怎么保证数据质量你都能给出具体回答。适合什么人做有Python基础语法知识、对爬虫和数据分析感兴趣、希望在毕业设计中兼顾技术深度和工程完整性的同学。基础要求并不高不需要你精通机器学习算法或者高深的统计学知识掌握pandas、requests、MySQL的基本操作再会用Flask搭个简单Web界面就已经能把这个项目做得相当完整了。我最初帮学生规划这个项目时也犹豫过要不要加一些花哨的功能——比如用LSTM预测歌曲热度或者用BERT做评论情感分析。后来全砍掉了。原因很现实毕业设计的核心是完整闭环而非算法炫技。你花两周调一个深度学习模型最后准确率也就60%答辩老师问我两句为什么选这个模型特征怎么设计的反而容易露怯。反而是把爬虫、存储、分析、可视化这条链路走通每一步都明明白白效果更好也更能体现你的工程能力。2. 系统整体设计与技术选型2.1 系统总体架构整个系统我采用经典的三层架构设计从上到下分别是数据采集层、数据分析层、应用展示层。每一层的职责边界非常清楚这也是我刻意追求的——毕业设计文档里要画架构图你分层设计越清晰后面写文档就越省力。数据采集层负责从网易云音乐官网抓取排行榜数据包括榜单名称、歌曲名称、歌手、专辑、时长、热度值等信息。分析层负责对采集到的原始数据进行清洗、去重、标准化然后按不同维度做统计分析。展示层我的选择是Flask ECharts的组合把分析结果以图表形式呈现在Web页面上同时提供简单的数据查询功能。为什么选Flask而不是Django很简单这个项目的后端逻辑不复杂没有用户权限管理、没有复杂的表单处理、也不需要admin后台。如果用Django光是熟悉它的目录结构和MTV模式就得花不少时间。Flask就清爽得多几十行代码就能把路由和页面渲染搞定而且Flask配ECharts的教程在网上非常多遇到问题搜起来也方便。当然如果你对Django已经很熟了用它也不是不行但在时间有限的情况下我的建议是选择够用且你上手最快的方案。至于为什么不用Scrapy而是用requests我可以坦白说Scrapy的异步框架和Item Pipeline设计很优雅对于大规模爬取来说效率确实高但它同时引入了学习成本——Spider、Middleware、Pipeline这些概念你都得理解才能搭建起一个完整的爬虫。这个项目的目标仅仅是采集几个排行榜、几百首歌的数据requests加BeautifulSoup的同步爬取足够了代码更直观调试也更容易。毕业设计不是生产环境不需要分布式爬虫也不需要每秒并发几十个请求用最简单的方式实现需求才是正道。2.2 功能模块划分系统的功能模块划分如下六个部分用户登录注册提供基本的用户认证功能支持用户注册、登录、会话管理是本系统访问控制的基础模块。数据采集模块面向管理员提供爬虫任务的启动与监控页面负责从网易云音乐公开接口抓取排行榜数据采集到的数据先以JSON格式落盘再批量入库。数据管理模块提供歌曲数据、榜单数据、歌手数据的浏览与检索功能同时支持数据备份和恢复操作。数据分析模块系统核心模块负责按不同维度统计分析数据生成分析结果包括排行榜特征分析、歌手热度分析、歌曲属性分析等。数据可视化模块将分析模块产出的结果以图表形式呈现包括榜单分布、歌手Top10、歌曲时长分布等图表。系统管理模块提供管理员账号管理、系统日志查看等功能。这里我特别想强调一点登录注册模块本来是计划外的需求是我在指导第一个学生时他主动加的。当时我还觉得没必要但后来发现加了这个模块之后系统的完整度明显上了一个档次——答辩的时候老师会看到你有用户体系、有权限区分管理员才能触发爬虫普通用户只能看分析结果这会让系统看起来更像一个真正能用的产品而不是课程作业。2.3 为什么选用Python全家桶既然题目就叫基于Python那技术栈自然是以Python为核心的。但这个选择并不仅仅是为了用Python而用Python而是因为Python在数据分析这条链路每一环都确实有成熟的解决方案。爬虫环节有requests、httpx这些老牌HTTP库解析HTML有BeautifulSoup、lxml解析JSON更是Python的看家本领。数据处理环节有pandas这个神器——DataFrame的数据结构能把表结构数据操作简化到极致groupby、merge、apply这些方法一组合大部分统计分析需求一两行代码就能搞定。数据可视化环节虽然Python也有matplotlib、pyecharts这些绘图库但既然要Web展示我宁愿直接用前端的ECharts——交互效果更好图表也更好看。我还想推荐一个大家可能不太注意的库schedule。这个库可以让你用最简单的方式配置定时任务——比如每天晚上8点自动触发一次爬虫更新当天的最新榜单数据。虽然对于毕业设计而言定时采集不是硬性需求但它能让你的系统展示出自动化运行的能力。答辩时一句我的系统支持定时自动更新数据效果比讲半天技术细节都好。关于数据库我的建议是用MySQL而不是SQLite。虽然SQLite部署简单、零配置但它在并发写入和数据容量方面的表现都比较弱——如果采集的数据量稍大或者多个请求同时写入SQLite偶尔会出现database is locked的报错。而MySQL作为最主流的关系型数据库之一装上之后你不仅能积累真实的数据库实战经验写文档时系统采用MySQL数据库存储数据这句话也更经得起推敲。数据量方面一个排行榜上百首歌全站几个榜单一共几千条记录MySQL完全是杀鸡用牛刀但你收获的却是一门实打实的技能。3. 数据采集与存储设计3.1 网易云排行榜数据接口分析网易云音乐的榜单数据有Web端页面、移动端App、开放API三种获取途径。对于爬虫而言优先考虑的是找到其内部API接口直接请求JSON数据效率远高于解析HTML页面。经过抓包分析网易云音乐的热歌榜数据接口格式如下# 请求URL https://music.163.com/api/v7/playlist/detail?id3778678 # 请求方法 GET # 请求头Headers Referer: https://music.163.com/ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36接口返回的JSON数据中包含playlist对象里面的tracks字段就是歌曲列表数组。每一首歌的track对象里有我们需要的大部分信息id歌曲ID、name歌名、ar歌手列表、al专辑信息、dt时长、pop热度值等。这里有个很关键的细节我找的是playlist/detail这个接口而不是某个专门针对排行榜的接口。原因是网易云音乐的排行榜本质上就是一个特殊的歌单——热歌榜对应歌单ID是3778678飙升榜是19723756新歌榜是3779629原创榜是2884035。既然它是歌单那我们只需要写一套针对歌单详情的爬虫逻辑把不同的榜单ID作为参数传进去就能复用非常优雅。我用requests写了一个FetchRankingData类来封装爬虫逻辑核心代码如下import requests import json import time class FetchRankingData: 网易云音乐排行榜数据采集器 # 榜单ID映射 RANK_IDS { 热歌榜: 3778678, 飙升榜: 19723756, 新歌榜: 3779629, 原创榜: 2884035 } def __init__(self): self.headers { Referer: https://music.163.com/, User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } self.base_url https://music.163.com/api/v7/playlist/detail def fetch_one_rank(self, rank_name: str, rank_id: str) - list: 采集单个榜单数据 params {id: rank_id} resp requests.get( self.base_url, paramsparams, headersself.headers, timeout10 ) # 接口返回的是JSON直接解析 data resp.json() tracks data.get(playlist, {}).get(tracks, []) songs [] for track in tracks: song { rank_name: rank_name, song_id: track.get(id), song_name: track.get(name), artist: /.join(a[name] for a in track.get(ar, [])), album: track.get(al, {}).get(name), duration_ms: track.get(dt), popularity: track.get(pop), } songs.append(song) return songs def fetch_all(self) - list: 采集所有榜单 all_songs [] for name, rid in self.RANK_IDS.items(): try: songs self.fetch_one_rank(name, rid) all_songs.extend(songs) print(f[OK] {name}: 采集 {len(songs)} 条) except Exception as e: print(f[ERR] {name}: 采集失败 - {e}) # 控制请求频率不要给服务器造成压力 time.sleep(1) return all_songs def save_to_json(self, songs: list, filepath: str): 保存原始数据到JSON文件 with open(filepath, w, encodingutf-8) as f: json.dump(songs, f, ensure_asciiFalse, indent2)这里我要专门解释两个容易被忽视的细节也是我踩过的坑。第一headers里必须带Referer。网易云音乐的API会校验请求来源如果你的请求头里没有Referer或者Referer不是music.163.com域名接口很可能返回403或者返回空的playlist对象。我一开始没带Referer结果requests请求响应状态码是200但拿到的playlist是空的排查了好久才发现是这个原因。第二请求频率一定要控制。我在爬虫里加了time.sleep(1)每个榜单之间间隔1秒。慢吗是有点慢四个榜单最多也就多花3秒。但这个慢是值得的——如果短时间内高频请求IP很可能被临时封禁一旦封禁你就得等几个小时反而更耽误事。爬虫的基本操守就是可以爬但别给人家服务器添麻烦。3.2 MySQL数据表结构设计数据库设计这块我建了三张表用户表、歌曲信息表、榜单抓取日志表。表结构如下-- 用户表 CREATE TABLE tb_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 歌曲信息表 CREATE TABLE tb_song ( id INT PRIMARY KEY AUTO_INCREMENT, rank_name VARCHAR(20) NOT NULL COMMENT 榜单名称, song_id BIGINT NOT NULL COMMENT 歌曲ID, song_name VARCHAR(100) NOT NULL COMMENT 歌曲名, artist VARCHAR(100) COMMENT 歌手, album VARCHAR(100) COMMENT 专辑, duration_ms INT COMMENT 时长(毫秒), popularity INT COMMENT 热度值, fetch_date DATE NOT NULL COMMENT 采集日期, UNIQUE KEY uk_song_rank_date (song_id, rank_name, fetch_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 采集日志表 CREATE TABLE tb_crawl_log ( id INT PRIMARY KEY AUTO_INCREMENT, rank_name VARCHAR(20) NOT NULL, fetch_count INT NOT NULL, fetch_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;在设计歌曲信息表的时候我特意加了一个联合唯一索引uk_song_rank_date。这个索引的作用是防止重复入库——同一首歌在同一天出现在同一个榜单里就应该只有一条记录。后续你重复运行爬虫时配合INSERT IGNORE或ON DUPLICATE KEY UPDATE语法就能非常优雅地做到有则更新、无则插入。为什么用utf8mb4而不是utf8因为歌曲名、歌手名里可能出现emoji字符或者生僻字MySQL的utf8编码最多只能存3字节的字符遇到emoji就会报错。utf8mb4是utf8的超集完全兼容4字节字符。这个问题在插入周杰伦的《Mojito》时特别容易遇到——歌名里的拉丁字符倒没事但某些歌手的名字里真的会带特殊符号。提前把字符集设置好能省掉后面处理编码问题的很多麻烦。3.3 数据清洗与预处理爬虫拿到的原始数据远没有想象中那么干净我在第一次采集后就发现了不少问题。清洗的过程大致分四步第一步去重处理。同一首歌可能同时出现在热歌榜和飙升榜里这是正常现象所以去重不能简单地按歌曲ID去重而是要按song_id rank_name fetch_date的组合判断。好在表结构里已经建了联合唯一索引插入时直接使用INSERT IGNORE就行。第二步缺失值处理。部分歌曲可能没有专辑信息或者duration_ms字段为空。对于缺失的专辑字段用未知专辑填充缺失的时长字段用该榜单的所有歌曲时长的均值填充。其实真实情况下字段缺失的情况比较少但你的代码里一定要有处理逻辑——答辩时老师问如果你的数据里有空值怎么办你就能直接回答我在清洗阶段用XX策略处理了。第三步格式标准化。把duration_ms从毫秒转成分:秒的格式便于展示把artist字段中多个歌手用/拼接成一个字符串规范化日期格式。第四步异常值处理。比如popularity字段理论上范围是0到100如果出现大于100或者负数的数据说明接口返回异常或字段解析有误直接剔除或标记为无效数据。关于数据清洗我写了一个DataCleaner类来做这件事。这里贴出标准化的核心方法import pandas as pd from datetime import datetime class DataCleaner: 数据清洗与标准化处理器 staticmethod def clean_dataframe(df: pd.DataFrame) - pd.DataFrame: # 1. 去掉完全重复的行 df df.drop_duplicates(subset[song_id, rank_name, fetch_date]) # 2. 缺失值处理 df[album] df[album].fillna(未知专辑) avg_duration df[duration_ms].mean() df[duration_ms] df[duration_ms].fillna(avg_duration) # 3. 时长字段标准化 - 单位转换为秒 df[duration_sec] (df[duration_ms] / 1000).astype(int) # 4. 异常值过滤 - 热度必须在0~100 df df[(df[popularity] 0) (df[popularity] 100)] # 5. 添加日期字段 df[fetch_date] pd.to_datetime(df[fetch_date]).dt.date return df另外再补充一个实际经验原始JSON文件一定要留底。清洗后的数据可以入库但原始数据最好以JSON或CSV形式保存一份。万一你在分析阶段发现清洗逻辑有bug还能回到原点重新处理不用重新跑爬虫。我的做法是每次采集后在data/raw目录下生成带时间戳的JSON文件比如raw_songs_20240610.json这样既能追溯数据源头也方便写文档时展示数据采集的原始记录。4. 数据分析维度与核心实现4.1 排行榜结构特征分析——谁在霸榜霸了多久数据分析的第一个维度是分析排行榜的整体结构特征。具体来说是研究四个榜单热歌榜、飙升榜、新歌榜、原创榜在歌曲构成上有什么不同以及是否存在霸榜现象。我用pandas做了几个关键统计各榜单歌曲数量分布四个榜单的规模不同通常是热歌榜100首、新歌榜100首但通过统计可以验证实际数量是否与预期一致。各榜单热度值分布用describe方法看popularity字段的均值、中位数、最大值、最小值可以判断哪个榜单的歌曲热度更高。多榜单交叉出现次数同一首歌出现在几个榜单里反映了它的综合热度。这里贴一段分析多榜单交叉出现的代码import pandas as pd # 读取数据 df pd.read_csv(clean_songs.csv) # 统计每首歌出现在几个榜单 song_rank_count ( df.groupby(song_id)[rank_name] .nunique() .reset_index() .rename(columns{rank_name: rank_count}) ) # 查看出现在3个及以上榜单的歌 hot_songs song_rank_count[song_rank_count[rank_count] 3] print(f出现在3个及以上榜单的歌曲数量: {len(hot_songs)}) # 关联歌曲名 song_info df[[song_id, song_name, artist]].drop_duplicates(song_id) result hot_songs.merge(song_info, onsong_id, howleft) print(result[[song_name, artist, rank_count]])这段代码的思路是先用groupby加nunique统计每首歌出现在几个不同的榜单然后筛选出出现在3个及以上榜单的歌曲。这些歌就是所谓的全网爆款——新歌榜上有名说明最近发布热歌榜上榜说明正在流行飙升榜上榜说明热度在快速上升。分析结果可以生成一个多榜霸榜歌曲Top10图表非常直观。4.2 歌手热度分析与作品分布第二个分析维度是歌手维度。核心问题是哪些歌手是当前网易云音乐上最热门的这些歌手在高热度榜单中占据了多大比例分析方法很简单按artist字段分组统计每个歌手在四个榜单中的作品总数和平均热度。但要提醒一个细节**多歌手合作的歌曲怎么归属**我在爬虫阶段就已经把多个歌手用/拼接成字符串了所以在统计时要么把这种合作歌曲当作一个整体计入这个组合名下要么用str.split(/)拆开后分别计入每个歌手的作品数。我采用后者理由是更符合直观感受——一首两人合唱的歌两个歌手都应该获得作品数加成。# 拆分合作歌手 df_expanded df.copy() df_expanded[artist] df_expanded[artist].str.split(/) df_expanded df_expanded.explode(artist) # 统计歌手作品数 artist_stats ( df_expanded.groupby(artist) .agg( song_count(song_id, count), avg_popularity(popularity, mean) ) .reset_index() .sort_values(song_count, ascendingFalse) ) print(artist_stats.head(10))这里用到了两个非常经典的pandas方法str.split配合explode来拆分行groupby配合agg做聚合统计。explode这个词挺形象它的作用就是把一个列表拆成多行其他字段自动复制到新行。这种方法在数据分析面试里也经常考到掌握了对后续求职也有帮助。4.3 歌曲属性分析——时长、热度与流行的关系第三个维度是歌曲自身属性分析。我最关注的两个特征是歌曲时长和歌曲热度值。歌曲时长分析方面我把所有歌曲按时长分为几个区间3分钟以下、3到4分钟、4到5分钟、5分钟以上然后统计各区间歌曲数量占比。之所以做这个分析是想验证一个普遍认知中等时长3-5分钟的歌曲是否在榜单中占绝大多数。分析结果显示确实如此这个规律背后反映的是音乐制作行业在歌曲结构设计上的一致性——前奏、主歌、副歌、桥段、尾声的经典结构时长一般就落在3到5分钟这个区间。歌曲热度分析方面我用热度值划分了高热度80分以上、中热度60-80分、低热度60分以下三档再看各榜单的热度分布差异。你可能会好奇为什么同一个数据集里不同榜单的热度值差异会很大因为不同榜单的歌曲是不同类型的——新歌榜收录的是最新发布的歌曲热度值可能还没积累起来热歌榜是当前播放量最高的歌热度值通常更高。这个分析能体现榜单位置和歌曲热度之间的关系。4.4 SQL查询统计分析除了用pandas做分析我也用SQL语句实现了部分统计功能。这样做的考虑是答辩时老师可能问你除了用Python处理数据会不会用SQL做查询统计如果你的答案是不会我只用pandas就会显得技能栈有点单薄。所以我把一部分相对简单的统计需求用SQL来实现既证明了数据库设计合理又展示了SQL能力。-- 各榜单歌曲数量统计 SELECT rank_name, COUNT(*) AS song_count FROM tb_song WHERE fetch_date 2024-06-10 GROUP BY rank_name; -- 歌手作品数量Top10 SELECT artist, COUNT(*) AS song_count FROM tb_song GROUP BY artist ORDER BY song_count DESC LIMIT 10; -- 平均热度最高的榜单 SELECT rank_name, AVG(popularity) AS avg_pop FROM tb_song GROUP BY rank_name ORDER BY avg_pop DESC;这三种查询方式各有使用场景SQL适合在数据库层面做快速验证pandas适合做复杂的数据加工和分析流程而最终的图表展示则是把分析结果翻译成用户能看懂的形式。三者不是互斥的而是配合使用。5. 数据可视化与Web系统搭建5.1 Flask项目结构与路由设计整个系统的Web框架用Flask构建项目结构如下NeteaseMusicAnalysis/ ├── app.py # Flask主程序入口 ├── config.py # 配置文件 ├── requirements.txt # 依赖包 ├── models/ │ ├── __init__.py │ ├── database.py # 数据库连接 │ └── user.py # 用户模型 ├── analysis/ │ ├── __init__.py │ └── analyzer.py # 分析引擎 ├── crawler/ │ ├── __init__.py │ └── fetcher.py # 爬虫模块 ├── static/ │ ├── css/ │ ├── js/ │ └── echarts.min.js # ECharts本地文件 ├── templates/ │ ├── base.html # 基础模板 │ ├── index.html # 首页/数据总览 │ ├── login.html # 登录页 │ ├── dashboard.html # 分析大屏 │ └── ... └── data/ ├── raw/ # 原始JSON数据 └── clean/ # 清洗后数据这个结构并不复杂但做到了职责分离爬虫代码和Web代码不混在同一个文件里分析逻辑独立成一个模块模板和静态资源各自归位。对于毕业设计来说这样的组织方式足够体现工程化意识了。路由设计方面我主要配置了以下几条/和/index数据总览首页展示系统基本信息和数据量统计/login和/register用户登录和注册/dashboard/overview排行榜总览大屏/dashboard/artist歌手热度分析页/dashboard/song歌曲属性分析页/crawl/start触发数据采集仅管理员可用/api/song_data返回歌曲数据JSON供前端图表调用5.2 ECharts可视化图表的实现说到可视化直接展示代码更直观。下面是一个从后端获取数据并在前端绘制歌手作品数Top10柱状图的完整实现。后端返回数据的接口app.route(/api/artist_top) def api_artist_top(): 返回歌手作品数Top10数据 analyzer Analyzer() top10 analyzer.artist_top_n(n10) return jsonify({ names: [item[artist] for item in top10], counts: [item[song_count] for item in top10] })前端JavaScript的ECharts渲染// 从后端获取数据 fetch(/api/artist_top) .then(response response.json()) .then(data { const chart echarts.init(document.getElementById(artistChart)); chart.setOption({ title: { text: 歌手作品数Top10, left: center }, tooltip: {}, xAxis: { type: category, data: data.names, axisLabel: { rotate: 30 } }, yAxis: { type: value, name: 作品数 }, series: [{ type: bar, data: data.counts, itemStyle: { color: #c23531 // 网易云红 }, barWidth: 50% }] }); });这里有一个小的设计细节值得注意x轴标签我加了axisLabel.rotate: 30让它旋转30度避免歌手名太长时互相遮挡。这种小细节在毕业设计答辩时是可以拿出来讲一讲的因为说明你考虑了真实使用场景下的可读性问题。ECharts的图表类型选择我也做了些规划。排行榜热度值分布用雷达图能同时展示多个榜单的综合对比各榜单歌曲数量占比用饼图直观明了歌手热度随时间变化用折线图可以展示动态趋势。再加上上文提到的柱状图和南丁格尔玫瑰图五类图表覆盖了分类对比、占比构成、趋势变化、多维度对比等主要可视化需求。5.3 可视化大屏的页面设计除了单个图表页面我还把核心图表整合到一个数据分析大屏页面中。如果你看过市面上FineBI、帆软做的BI大屏对这个概念就不会陌生——把多个图表组合在一个页面里用网格布局排布形成一眼看全的效果。大屏的布局我设计如下顶部系统标题与数据更新日期左侧各榜单歌曲数量占比饼图 榜单平均热度对比雷达图中间多榜霸榜歌曲Top10表格 歌手作品数Top10柱状图右侧歌曲时长分布柱状图 热度区间分布饼图底部榜单热度均值、歌曲总数、歌手总数等核心指标卡片大屏页面的数据全部通过AJAX异步加载图表初始化时显示加载动画数据返回后由ECharts渲染。为了让大屏展示效果更好我还在CSS中设置了深色背景主题配合ECharts的亮色图表视觉上层次分明。做这个大屏页面的另一个考虑是它在答辩现场特别出效果。你把笔记本投影到屏幕上打开这个大屏页面不用多说话老师就能直观感受到这个系统的能力边界。相信我这比打开Excel表格给老师看一堆数字要加分太多。6. 系统部署、测试与常见问题6.1 本地部署与运行指引部署这套系统你需要准备的环境包括Python 3.8及以上版本、MySQL 5.7及以上版本、以及requirements.txt里列出的所有Python依赖包。依赖安装命令如下pip install flask requests pandas beautifulsoup4 pymysql sqlalchemy如果你使用的是国内网络环境pip安装速度慢的话可以临时切换为国内镜像源pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask requests pandas数据库准备步骤是启动MySQL服务用source命令导入项目中的init.sql文件创建数据库和表结构然后在config.py中修改数据库连接信息用户名、密码、数据库名。运行python app.py后在浏览器中访问http://127.0.0.1:5000即可看到系统首页。如果你没有安装MySQL也没有关系项目里的数据库操作层用了SQLAlchemy ORM你可以很轻松地把数据库连接切换到SQLite# config.py 中修改数据库连接 # MySQL连接 SQLALCHEMY_DATABASE_URI mysqlpymysql://root:passwordlocalhost:3306/music_db # SQLite连接测试环境用 # SQLALCHEMY_DATABASE_URI sqlite:///music.db两种数据库的切换只需要改一行配置这是SQLAlchemy的好处。不过我还是建议你同时安装MySQL并测试一遍毕竟毕业设计环境通常要求使用主流数据库提前熟悉没有坏处。6.2 系统测试过程与方法系统测试我采用的是模块测试 集成测试两步走的方式没有用自动化测试框架而是手动逐模块验证。虽然手动测试听起来不够高级但对于这种规模的项目手动测试反而能发现更多直观问题。我把测试用例整理成了表格方便在文档中写入测试分析章节测试模块测试内容预期结果测试结果用户模块注册新用户用户名密码入库提示注册成功正常用户模块登录验证输入正确密码跳转首页错误密码提示失败正常爬虫模块采集热歌榜数据接口返回100条歌曲信息正常爬虫模块重复采集同一榜单不产生重复数据正常分析模块统计歌手作品数Top10与SQL查询结果一致正常可视化模块加载图表数据图表正常渲染无空白页正常安全模块未登录访问大屏页面跳转登录页正常测试过程中最容易忽略的是异常场景测试——比如断网情况下启动爬虫系统会不会崩溃数据库中无数据时打开大屏页面前端会不会报错。这些场景虽然不常出现但一旦出现就能看出系统设计是否健壮。我的处理方式是在代码里加try-except异常捕获在爬虫函数里对网络异常做重试处理在前端对空数据做空状态提示。6.3 常见问题与排查技巧实录问题一爬虫采集时返回请求被拒绝或数据为空这是我在实际开发中遇到的第一个大坑。现象是requests请求返回200状态码但响应内容里的playlist对象为空。排查过程我先用浏览器直接访问接口URL发现可以正常返回数据说明接口本身没问题然后用curl模拟requests的请求头发现只要加了Referer为music.163.com就能正常返回。最终确认是请求头缺Referer被服务端校验拦截。解决办法就是在请求头中加上Referer和完整的User-Agent。问题二中文乱码问题现象是HTML页面和图表中显示乱码或者数据库中的数据混乱。排查下来发现是两个层面的原因MySQL表或连接字符集不是utf8mb4导致中文字符被错误编码或者是Python脚本文件本身没有声明utf-8编码。解决办法分别是对应的建表语句中统一使用DEFAULT CHARSETutf8mb4并在数据库连接URL中加上?charsetutf8mb4参数Python文件在文件头部声明# -- coding: utf-8 --。问题三pip安装依赖时超时失败这个问题的根源是默认PyPI源在部分地区访问不稳定。可以临时换用国内镜像源或者给pip配置全局镜像地址pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple问题四ECharts图表不显示控制台报echarts is not defined通常是因为ECharts的JavaScript文件没有正确加载。我建议下载echarts.min.js放到项目本地static/js目录下而不是直接引入CDN链接——因为答辩时的场地网络不一定稳定一旦断网图表就全没了用本地文件可以确保万无一失。问题五连接MySQL数据库报Access denied for user一般是数据库用户名或密码错误或者用户名没有远程访问权限。检查config.py中的数据库连接配置即可解决。6.4 避坑指南与提升建议最后说几个只有做完整套流程才能体会到的经验。第一排行榜接口选择要小心。网易云音乐的接口经历过多次调整网上教程里很多接口已经失效了。如果你跟着旧教程写可能爬不到数据。我的建议是在动手写代码之前先用浏览器F12打开开发者工具手动访问官网排行榜页面观察Network面板里实际发了哪些请求、返回了什么数据以你亲眼看到的最新请求为准。这个过程浪费不了多长时间但能让你避免被过时教程误导。第二先爬后析、边做边存。不要等把所有代码写完了再一次性运行。先把爬虫模块单独跑通拿到一份真实数据存到CSV里再用jupyter notebook加载这份CSV做分析和可视化确认分析逻辑没问题最后才是整合进Flask系统。这样分阶段推进每个环节出现问题都能准确定位。第三如果时间允许给系统加一点差异化功能。因为做网易云数据分析的人太多了如果只是千篇一律的爬虫图表答辩时很难有亮点。我觉得容易实现且效果不错的扩展方向有做一个基于分类或聚类的歌曲推荐功能或者接入词云库jieba和wordcloud对歌曲评论做热词分析生成词云图或者做一个简单的爬虫监控页面展示每次采集的时间、数量、成功率。一个亮点功能就够让系统脱颖而出。第四论文和项目代码要同步推进。很多毕业生犯的错误是把代码全部做完再开始写论文结果发现代码和文档对不上又回头改代码浪费时间。我的建议是每完成一个模块就同步更新论文对应章节这样到最后论文初稿就已经完成了80%。毕竟毕业设计的评分标准里论文和答辩表现所占的权重可不比代码本身低。7. 写在最后的心里话做完这个项目我最深的一个感受是毕业设计的价值不在于你用到的技术有多前沿而在于你有没有完整体验一遍从需求分析到系统交付的全过程。爬虫怎么设计、数据怎么清洗、系统怎么分层、文档怎么写、答辩怎么讲——这些能力才是你未来进入职场后真正用得上的东西。如果你正在为选题纠结或者已经选了类似题目但感觉无从下手希望这篇内容能帮你理清思路。按照数据采集 → 数据存储 → 数据分析 → 数据可视化 → 系统整合这条主线一步一步来不要着急每完成一个模块你都会获得实实在在的成就感。遇到问题的时候优先查看日志和错误信息多半能定位到问题所在自己解决不了就去搜索或请教这本身就是程序员必备的生存技能。最后再分享一个实操小技巧在答辩演示时建议提前准备好一份真实数据并加载到系统里当场演示启动爬虫虽然更真实但万一现场网络状况不佳导致采集失败多少会影响发挥。稳妥的做法是演示页面展示已有的分析结果同时准备好爬虫模块的录屏或截图作为补充材料。这不是让你弄虚作假而是用工程思维做好风险预案——毕竟在真实的工作环境中给客户做演示之前你也会做同样的准备。
返回列表