拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Python数据分析实战:构建音乐榜单追踪系统

如果你是一位音乐行业的数据分析师或者是一位想用技术手段追踪流行文化趋势的开发者你可能会发现一个痛点如何系统性地、自动化地追踪一首热门单曲在全球各大音乐榜单上的实时表现手动每天去 Spotify 和 Billboard 官网记录排名不仅效率低下而且难以进行趋势分析和可视化。这正是数据驱动洞察的起点。本文将以 Olivia Rodrigo 的热门单曲《Good 4 u》为例手把手教你构建一个从数据爬取、清洗、存储到可视化分析的完整技术栈。你将学到的不是简单的“看排名”而是如何用 Python、Pandas、SQL 和可视化库打造一个属于自己的音乐榜单追踪与分析系统。我们会先剖析《Good 4 u》的榜单数据背后隐藏的技术需求然后分步实现一个轻量级但功能完整的项目。读完本文你将能够理解音乐榜单数据的公开获取途径与限制。使用 Python 爬虫在合规前提下或 API 获取结构化数据。利用 Pandas 进行数据清洗与时间序列分析。使用 SQLite 或 PostgreSQL 持久化存储历史数据。通过 Matplotlib 或 Plotly 绘制专业的位次推移图表。掌握数据分析的核心思路并可将此框架复用于其他歌曲或榜单。1. 项目核心从数据需求到技术方案拆解我们的目标是分析《Good 4 u》在 Spotify 全球日榜和 Billboard Hot 100 上的位次变化。这直接引出了几个技术问题数据源在哪里Spotify 有公开的“每日热门歌曲”榜单但可能没有直接的历史排名API。Billboard 官网有榜单但需要解析HTML。数据如何获取是手动收集、使用官方API如有、还是通过网页爬虫我们必须考虑法律合规性与网站的反爬策略。数据如何存储每日的排名是一个时间序列数据需要设计合理的数据表结构来存储日期、榜单名称、排名、歌曲信息等。数据如何分析我们关心最高排名、在榜周数、排名下降速度、平台间排名差异等指标。结果如何呈现需要生成清晰的折线图对比两个平台的排名趋势。因此技术方案链路如下数据采集 → 数据清洗与格式化 → 数据存储 → 数据分析 → 数据可视化。下面我们将按照这个链路一步步实现。2. 环境准备与工具选型在开始写代码之前我们需要搭建好开发环境。本项目主要使用 Python因其在数据分析和爬虫领域的强大生态。2.1 基础环境操作系统Windows 10/11, macOS, 或 Linux (如 Ubuntu) 均可。Python 版本建议使用 Python 3.8 及以上版本。我们将使用一些较新的语法和库特性。2.2 核心 Python 库我们将使用pip来安装以下库。请打开你的终端或命令提示符执行以下命令# 创建虚拟环境推荐避免包冲突 python -m venv music_chart_env # 激活虚拟环境 # Windows: music_chart_env\Scripts\activate # macOS/Linux: source music_chart_env/bin/activate # 安装核心库 pip install requests beautifulsoup4 pandas matplotlib plotly sqlalchemy库的作用简介requests: 用于发送 HTTP 请求获取网页内容或 API 数据。beautifulsoup4: 用于解析 HTML 网页提取结构化数据当没有现成 API 时。pandas: 数据分析的核心用于数据清洗、处理和计算。matplotlib: 基础绘图库用于生成静态图表。plotly: 交互式绘图库可以生成更美观、可交互的图表。sqlalchemy: Python 的 SQL 工具包和对象关系映射ORM工具用于连接和操作数据库。2.3 数据存储选择对于个人项目或中小规模数据SQLite是最简单轻量的选择它是一个文件数据库无需安装额外服务。如果考虑未来扩展或团队协作可以使用PostgreSQL或MySQL。 本文将以 SQLite 为例进行演示。3. 数据采集策略与模拟数据生成由于直接爬取 Spotify 和 Billboard 官网可能违反其服务条款且面临反爬机制为了专注于教学和流程演示我们将采取以下策略理想情况API寻找官方或第三方提供的稳定 API如 Spotify Web API但需要认证和申请且可能不直接提供历史日榜。实际情况教学演示我们将模拟生成一份符合真实趋势的《Good 4 u》榜单数据。这能让我们绕过复杂的爬虫和反爬问题聚焦于核心的数据处理与分析流程。我们假设《Good 4 u》在发布后迅速登顶随后缓慢下降并且在两个榜单上的趋势类似但略有差异。3.1 创建模拟数据脚本创建一个名为generate_mock_data.py的文件。# generate_mock_data.py import pandas as pd import numpy as np from datetime import datetime, timedelta def generate_chart_data(song_name, start_date, peak_rank, peak_date_offset, weeks_on_chart, noise_level5): 生成模拟的榜单数据。 参数: song_name: 歌曲名 start_date: 首次上榜日期 (datetime) peak_rank: 最高排名 (1-100) peak_date_offset: 在第几天达到峰值 (整数) weeks_on_chart: 在榜周数 noise_level: 排名波动的噪声大小 dates [] ranks [] current_date start_date total_days weeks_on_chart * 7 # 模拟一个先快速上升后缓慢下降并带有噪声的趋势 for day in range(total_days): dates.append(current_date) # 使用一个简化的模型来模拟排名变化 # 阶段1: 上升期 (到达峰值) if day peak_date_offset: # 从100名外快速上升到峰值 trend_rank 100 - (day / peak_date_offset) * (100 - peak_rank) else: # 阶段2: 下降期 (从峰值缓慢下降) decay_days day - peak_date_offset # 下降速度逐渐变缓 trend_rank peak_rank (decay_days ** 0.7) / (total_days / 20) # 添加随机噪声模拟日常波动 noisy_rank trend_rank np.random.normal(0, noise_level) # 确保排名在1-100之间并取整 final_rank int(np.clip(noisy_rank, 1, 100)) ranks.append(final_rank) current_date timedelta(days1) df pd.DataFrame({ date: dates, rank: ranks, song: song_name }) return df # 生成《Good 4 u》在两个榜单的数据 # 假设歌曲于2021年5月21日发布并迅速上榜 start_date datetime(2021, 5, 21) # Spotify 全球日榜模拟峰值更高下降稍快 spotify_df generate_chart_data( song_nameGood 4 u - Olivia Rodrigo, start_datestart_date, peak_rank1, # 在Spotify全球日榜登顶 peak_date_offset7, # 发布后第7天登顶 weeks_on_chart30, # 在榜约30周 noise_level3 # 波动较小 ) spotify_df[chart] Spotify Daily Global # Billboard Hot 100 模拟峰值可能非第一下降更平缓榜单计算方式不同 billboard_df generate_chart_data( song_nameGood 4 u - Olivia Rodrigo, start_datestart_date, peak_rank1, # 在Billboard Hot 100也登顶 peak_date_offset14, # 发布后第14天登顶流媒体、电台、销量综合计算有延迟 weeks_on_chart35, # 在榜周数更长 noise_level5 # 波动稍大 ) billboard_df[chart] Billboard Hot 100 # 合并数据 combined_df pd.concat([spotify_df, billboard_df], ignore_indexTrue) # 保存为CSV文件供后续步骤使用 combined_df.to_csv(good4u_chart_data.csv, indexFalse) print(模拟数据已生成并保存到 good4u_chart_data.csv) print(combined_df.head())运行这个脚本你将在当前目录下得到一个good4u_chart_data.csv文件它包含了模拟的、带有一定趋势和随机性的榜单数据。4. 数据清洗、存储与数据库设计拿到数据无论是模拟的还是爬取的后第一步是清洗和存入数据库便于管理和后续查询。4.1 使用 Pandas 进行初步清洗创建一个名为data_pipeline.py的文件。# data_pipeline.py import pandas as pd from sqlalchemy import create_engine, Column, Integer, String, Date from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from datetime import datetime # 1. 读取模拟数据 df pd.read_csv(good4u_chart_data.csv) print(原始数据预览:) print(df.head()) print(f\n数据形状: {df.shape}) # 2. 数据清洗 # 确保日期列是datetime类型 df[date] pd.to_datetime(df[date]) # 检查缺失值 print(f\n缺失值统计:\n{df.isnull().sum()}) # 去除可能的重复行同一天同一榜单 df df.drop_duplicates(subset[date, chart, song]) # 按日期和榜单排序 df df.sort_values(by[chart, date]).reset_index(dropTrue) print(\n清洗后数据预览:) print(df.head()) # 3. 设计数据库模型 (使用SQLAlchemy ORM) Base declarative_base() class ChartRecord(Base): 榜单记录数据表模型 __tablename__ chart_records id Column(Integer, primary_keyTrue, autoincrementTrue) song_name Column(String(255), nullableFalse) chart_name Column(String(100), nullableFalse) # 如 ‘Spotify Daily Global‘ rank Column(Integer, nullableFalse) # 排名1-100 record_date Column(Date, nullableFalse) # 榜单日期 # 可以添加更多字段如流媒体播放量、国家地区等 def __repr__(self): return fChartRecord(song{self.song_name}, chart{self.chart_name}, rank{self.rank}, date{self.record_date}) # 4. 连接数据库并创建表 # 使用SQLite数据库文件 engine create_engine(sqlite:///music_charts.db) Base.metadata.create_all(engine) # 创建表 # 5. 将Pandas DataFrame数据写入数据库 # 首先将DataFrame的列名映射到数据库模型的字段名 df_to_insert df.rename(columns{ song: song_name, chart: chart_name, rank: rank, date: record_date }) # 使用Pandas的to_sql方法批量插入如果表存在则替换 df_to_insert.to_sql(ChartRecord.__tablename__, engine, if_existsreplace, indexFalse) print(f\n数据已成功写入数据库 ‘music_charts.db‘ 的 ‘{ChartRecord.__tablename__}‘ 表中。) # 6. (可选) 验证数据插入 Session sessionmaker(bindengine) session Session() record_count session.query(ChartRecord).count() print(f数据库中总记录条数: {record_count}) # 查询Spotify榜单最早和最新的记录 spotify_records session.query(ChartRecord).filter_by(chart_nameSpotify Daily Global).order_by(ChartRecord.record_date).all() if spotify_records: print(fSpotify最早记录: {spotify_records[0]}) print(fSpotify最新记录: {spotify_records[-1]}) session.close()这个脚本完成了以下工作读取数据从CSV文件加载。清洗数据转换日期格式、去重、排序。定义数据模型使用 SQLAlchemy ORM 定义了ChartRecord表这是将业务概念一条榜单记录映射到数据库表的关键。建立数据库连接创建了一个 SQLite 数据库文件music_charts.db。批量插入数据利用 Pandas 的高效接口将清洗后的数据写入数据库。验证简单查询确认数据已入库。5. 数据分析计算关键指标数据存好了现在我们可以进行真正的分析。我们关心哪些指标首日排名歌曲首次上榜的位置。峰值排名Peak Position及到达日期歌曲达到的最高排名。在榜时长Weeks on Chart歌曲停留在榜单内的周数。排名稳定性排名波动的标准差。平台对比同一首歌在不同榜单上排名趋势的差异。创建一个名为analyze_charts.py的文件。# analyze_charts.py import pandas as pd from sqlalchemy import create_engine, func from sqlalchemy.orm import sessionmaker from data_pipeline import ChartRecord # 导入之前定义的数据模型 import matplotlib.pyplot as plt import plotly.express as px import plotly.graph_objects as go from plotly.subplots import make_subplots # 连接数据库 engine create_engine(sqlite:///music_charts.db) Session sessionmaker(bindengine) session Session() # 1. 将数据从数据库读回Pandas DataFrame便于分析 query session.query(ChartRecord) df pd.read_sql(query.statement, engine) session.close() # 确保日期类型 df[record_date] pd.to_datetime(df[record_date]) # 2. 按榜单分组计算关键指标 def calculate_metrics(group_df): 计算单个榜单的关键指标 metrics { ‘chart_name‘: group_df[‘chart_name‘].iloc[0], ‘song_name‘: group_df[‘song_name‘].iloc[0], ‘first_date‘: group_df[‘record_date‘].min(), ‘last_date‘: group_df[‘record_date‘].max(), ‘peak_rank‘: group_df[‘rank‘].min(), # 排名数字越小越好 ‘peak_date‘: group_df.loc[group_df[‘rank‘].idxmin(), ‘record_date‘], ‘first_rank‘: group_df[‘rank‘].iloc[0], ‘last_rank‘: group_df[‘rank‘].iloc[-1], ‘days_on_chart‘: (group_df[‘record_date‘].max() - group_df[‘record_date‘].min()).days 1, ‘weeks_on_chart‘: ((group_df[‘record_date‘].max() - group_df[‘record_date‘].min()).days // 7) 1, ‘avg_rank‘: group_df[‘rank‘].mean(), ‘rank_std‘: group_df[‘rank‘].std(), # 标准差衡量波动性 } return pd.Series(metrics) # 对每个榜单进行计算 chart_metrics df.groupby(‘chart_name‘).apply(calculate_metrics).reset_index(dropTrue) print(“关键指标汇总“) print(chart_metrics.to_string(indexFalse)) # 3. 深入分析《Good 4 u》在两个榜单上的表现对比 print(“\n--- 《Good 4 u》榜单表现深度对比 ---“) for _, row in chart_metrics.iterrows(): chart row[‘chart_name‘] print(f“\n在 **{chart}**“) print(f“ • 首日排名第{int(row[‘first_rank‘])}名“) print(f“ • 峰值排名第{int(row[‘peak_rank‘])}名于{row[‘peak_date‘].date()}达成“) print(f“ • 在榜时长{row[‘weeks_on_chart‘]}周共{row[‘days_on_chart‘]}天“) print(f“ • 平均排名第{row[‘avg_rank‘]:.1f}名“) print(f“ • 排名波动性标准差{row[‘rank_std‘]:.2f}值越小越稳定“) # 判断哪个榜单表现“更好”排名数字更小 if not chart_metrics.empty: best_chart chart_metrics.loc[chart_metrics[‘peak_rank‘].idxmin()] print(f“\n**结论**歌曲在 **{best_chart[‘chart_name‘]}** 上达到了更高的峰值第{int(best_chart[‘peak_rank‘])}名。“)运行此脚本你将得到一份清晰的指标对比报告。这比单纯看图表更直观地揭示了歌曲在不同平台的生命周期和受欢迎程度差异。6. 数据可视化绘制位次推移图数字是骨肉图表是灵魂。我们将用两种方式绘制排名趋势图静态图Matplotlib和交互式图表Plotly。6.1 使用 Matplotlib 绘制静态对比图继续在analyze_charts.py文件中添加以下代码# analyze_charts.py (续) # 4. 使用 Matplotlib 绘制静态趋势对比图 plt.figure(figsize(14, 7)) charts df[‘chart_name‘].unique() colors [‘steelblue‘, ‘coral‘] # 为不同榜单分配颜色 for idx, chart in enumerate(charts): chart_data df[df[‘chart_name‘] chart].copy() chart_data chart_data.sort_values(‘record_date‘) plt.plot(chart_data[‘record_date‘], chart_data[‘rank‘], labelchart, colorcolors[idx], linewidth2, marker‘.‘, markersize5) plt.gca().invert_yaxis() # 反转Y轴让排名1在顶部 plt.xlabel(‘日期‘, fontsize12) plt.ylabel(‘排名数字越小越好‘, fontsize12) plt.title(‘Olivia Rodrigo - 《Good 4 u》榜单位次推移对比‘, fontsize16, fontweight‘bold‘) plt.legend(loc‘upper left‘) plt.grid(True, linestyle‘--‘, alpha0.6) plt.xticks(rotation45) plt.tight_layout() # 保存图片 plt.savefig(‘good4u_chart_trend_matplotlib.png‘, dpi300) print(“\n静态趋势图已保存为 ‘good4u_chart_trend_matplotlib.png‘“) # plt.show() # 如果在本地Jupyter或IDE中运行可以显示6.2 使用 Plotly 绘制交互式图表交互式图表允许用户悬停查看具体日期的排名缩放区域更利于探索。# analyze_charts.py (续) # 5. 使用 Plotly 绘制交互式趋势对比图 fig px.line(df, x‘record_date‘, y‘rank‘, color‘chart_name‘, title‘Olivia Rodrigo - 《Good 4 u》榜单位次推移对比交互式‘, labels{‘record_date‘: ‘日期‘, ‘rank‘: ‘排名‘, ‘chart_name‘: ‘榜单‘}, line_shape‘linear‘, # 也可以尝试 ‘spline‘ 让曲线更平滑 markersTrue) # 优化图表样式 fig.update_yaxes(autorange“reversed“, title_text“排名数字越小越好“) # 反转Y轴 fig.update_xaxes(title_text“日期“) fig.update_layout( hovermode‘x unified‘, # 悬停时显示同一x轴日期的所有y值 template‘plotly_white‘, height600, legenddict(orientation“h“, yanchor“bottom“, y1.02, xanchor“right“, x1) ) # 保存为HTML文件可在浏览器中打开交互 fig.write_html(‘good4u_chart_trend_plotly.html‘) print(“交互式趋势图已保存为 ‘good4u_chart_trend_plotly.html‘用浏览器打开即可查看。“) # 也可以在一个图中创建子图更清晰地对比 fig_subplots make_subplots(rows2, cols1, subplot_titles(“Spotify Daily Global“, “Billboard Hot 100“), shared_xaxesTrue) for idx, chart in enumerate(charts, start1): chart_data df[df[‘chart_name‘] chart].copy() chart_data chart_data.sort_values(‘record_date‘) fig_subplots.add_trace( go.Scatter(xchart_data[‘record_date‘], ychart_data[‘rank‘], mode‘linesmarkers‘, namechart), rowidx, col1 ) fig_subplots.update_yaxes(autorange“reversed“, rowidx, col1) fig_subplots.update_layout(height800, title_text“《Good 4 u》在不同榜单的独立位次推移“) fig_subplots.write_html(‘good4u_chart_trend_subplots.html‘) print(“子图对比趋势图已保存为 ‘good4u_chart_trend_subplots.html‘。“)运行完整的analyze_charts.py脚本后你将在当前目录得到一个文本格式的关键指标汇总。一张 PNG 格式的静态趋势对比图 (good4u_chart_trend_matplotlib.png)。两个 HTML 格式的交互式图表用浏览器打开即可进行缩放、悬停查看等操作。7. 项目扩展与高级分析思路基础分析完成后我们可以思考如何让这个项目更强大、更实用。7.1 接入真实数据源谨慎操作Billboard 使用billboard.py等第三方库需遵守其条款或直接解析 Billboard 官网注意robots.txt和反爬。Spotify 使用官方 Spotify Web API 。你需要注册开发者账号创建应用以获取Client ID和Client Secret并通过 OAuth 2.0 流程获取访问令牌。API 可以获取歌曲的流行度指数0-100但并非直接的日榜排名需要自己根据流行度排序或寻找其他数据源。数据聚合平台 如 Chartmetric、SoundCharts 等商业平台提供丰富的 API但通常收费。重要提醒任何爬取或调用 API 的行为都必须严格遵守目标网站的服务条款、robots.txt文件规定以及相关法律法规。对于个人学习和研究务必控制请求频率避免对目标服务器造成负担。7.2 构建自动化数据管道你可以使用schedule库或操作系统的定时任务如cron或 Windows 任务计划程序让数据采集脚本每天自动运行一次将最新排名追加到数据库中。# 示例简单的定时任务思路 (schedule库) import schedule import time def daily_job(): print(“开始执行每日数据更新...“) # 1. 调用数据采集函数模拟或真实 # new_data fetch_real_data() # 2. 清洗数据 # cleaned_data clean_data(new_data) # 3. 存入数据库 # save_to_db(cleaned_data) print(“数据更新完成。“) # 每天上午10点执行 schedule.every().day.at(“10:00“).do(daily_job) while True: schedule.run_pending() time.sleep(60)7.3 增加更多分析维度多歌曲对比 在数据库中存储多首歌的数据分析同一歌手不同歌曲的生命周期或同期竞争歌曲的排名走势。预测模型 基于历史排名数据尝试使用时间序列模型如 ARIMA、LSTM预测歌曲未来的排名趋势。关联分析 尝试寻找排名变化与外部事件如音乐视频发布、综艺节目表演、社交媒体热点的关联性。构建Web仪表盘 使用Flask或Streamlit快速搭建一个Web应用将数据库查询和 Plotly 图表集成进去实现一个实时监控仪表盘。8. 常见问题与排查思路在实践过程中你可能会遇到以下问题问题现象可能原因排查方式解决方案运行generate_mock_data.py时报错ModuleNotFoundError虚拟环境未激活或依赖未安装在终端检查当前环境运行pip list查看是否安装了所需库激活虚拟环境并执行pip install -r requirements.txt如果你创建了该文件或重新安装数据库操作失败提示OperationalError数据库文件被占用或路径错误检查music_charts.db文件是否被其他程序如DB浏览器打开关闭占用程序或确保create_engine中的文件路径正确Plotly 图表保存为 HTML 后打开空白浏览器安全策略或文件路径包含中文等特殊字符检查浏览器控制台F12是否有错误检查HTML文件是否成功生成尝试使用绝对路径保存文件或确保文件名和路径为英文从数据库读取的数据为NaN或类型错误数据清洗步骤不彻底或数据库字段类型不匹配使用df.info()和df.head()检查数据类型和内容在数据清洗步骤加强类型转换和缺失值处理如fillna图表Y轴反转后标签显示不正确Matplotlib 的invert_yaxis()只反转轴不反转标签检查图表排名1是否在顶部使用plt.gca().invert_yaxis()后排名顺序已正确标签“数字越小越好”已说明想分析真实数据但不知道如何入手对网站结构或API不熟悉使用浏览器开发者工具F12的“网络”选项卡观察API请求或查看网页HTML结构先从简单的、结构清晰的公开数据源开始练习如一些提供JSON格式的公开API9. 最佳实践与项目总结通过这个项目我们不仅分析了《Good 4 u》的榜单趋势更实践了一个标准的数据分析流水线明确目标与设计 始终从业务问题“歌曲排名如何变化”出发设计技术方案数据从哪来、怎么存、如何分析。模拟数据先行 在获取真实数据困难或风险高时用模拟数据验证整个流程是最高效的方式。重视数据存储 即使数据量小也使用数据库如SQLite而非纯文件这为未来的查询、扩展和自动化打下基础。分析驱动可视化 先计算关键指标峰值、在榜时长再绘图。图表是为了更好地传达分析结论而不是终点。代码模块化 将数据生成、清洗、存储、分析、可视化分到不同脚本或函数中方便调试和复用。选择合适工具 快速探索用 Plotly生成报告用 Matplotlib轻量级用 SQLite复杂关系用 PostgreSQL。下一步你可以将本项目的代码封装成类使其能方便地接入新的歌曲或榜单。尝试用Streamlit在半天内构建一个交互式仪表盘让没有编程背景的人也能查看分析结果。探索更复杂的分析比如计算歌曲的“榜单生命力指数”结合峰值、在榜时长、下降斜率。这个项目是一个模板其核心价值在于将模糊的文化现象一首歌火不火转化为可量化、可分析、可追溯的数据工程问题。你可以将这套方法应用到任何有时间序列特征的排行榜分析中例如 App Store 应用排名、GitHub 趋势仓库、电影票房日榜等。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门