最近在做一个挺有意思的小项目——用Python抓取网易云音乐排行榜数据并进行分析。这个需求其实源于我自己的日常习惯,作为重度音乐爱好者,每周都会查看网易云的各种榜单,但总觉得单纯听歌少了点什么。如果能系统性地分析这些榜单数据,或许能发现一些有趣的音乐市场规律。
这个系统主要解决三个实际问题:一是自动化获取最新榜单数据,避免手动记录;二是对音乐流行趋势进行多维度分析;三是生成可视化报告帮助理解数据。特别适合音乐行业从业者、数据分析爱好者以及想了解Python爬虫与数据分析结合实战的朋友参考。
从技术角度看,项目完整覆盖了Python生态中的几个关键环节:网络请求处理、反爬策略应对、数据清洗转换、分析模型构建以及可视化呈现。下面我会详细拆解每个环节的实现思路和具体操作,过程中遇到的坑和解决方案也会一并分享。
核心采用Python 3.8+环境,主要依赖库包括:
选择这个组合主要基于三点考虑:首先,网易云音乐页面结构相对复杂但API接口规范,需要灵活的解析工具;其次,pandas在时间序列分析上有天然优势;最后,轻量级架构便于后期扩展其他音乐平台数据。
系统工作流程分为四个阶段:
关键设计原则:各层之间通过明确的数据接口隔离,比如采集层统一输出DataFrame格式,这样后续替换存储或分析模块时不会产生连锁影响。
网易云音乐排行榜数据主要通过两个官方接口获取:
https://music.163.com/api/playlist/detail?id={榜单ID}https://music.163.com/api/song/detail?ids=[歌曲ID]通过Chrome开发者工具抓包可以发现,请求需要携带几个关键头部:
python复制headers = {
'Referer': 'https://music.163.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Cookie': '你的登录cookie' # 非必须但能提高成功率
}
实测会遇到三个典型反爬问题:
解决方案组合:
time.sleep(random.uniform(0.5, 1.5))控制请求间隔python复制def safe_request(url, max_retry=3):
for i in range(max_retry):
try:
resp = requests.get(url, headers=headers, timeout=10)
if resp.status_code == 200:
return resp.json()
elif resp.status_code == 418:
time.sleep(2**i) # 指数退避
except Exception as e:
print(f"Request failed: {str(e)}")
return None
原始数据需要经过以下清洗步骤:
python复制def clean_data(raw_df):
# 转换时间戳
raw_df['publish_time'] = pd.to_datetime(raw_df['publish_time'], unit='ms')
# 处理播放量(可能包含"万"单位)
raw_df['play_count'] = raw_df['play_count'].apply(
lambda x: float(x.replace('万',''))*10000 if '万' in str(x) else float(x)
)
# 提取风格标签
raw_df['styles'] = raw_df['song_detail'].apply(extract_styles)
return raw_df
构建了四个分析模型:
其中歌手影响力指数的计算公式为:
code复制影响力 = Σ( (榜单长度 - 排名位置 + 1) * 停留天数 / 榜单长度 )
使用matplotlib的面向对象API实现可复用的绘图组件:
python复制class TrendPlotter:
def __init__(self, df):
self.df = df
def plot_style_distribution(self, save_path=None):
fig, ax = plt.subplots(figsize=(12, 6))
style_counts = self.df['styles'].explode().value_counts()
style_counts[:10].plot(kind='barh', ax=ax)
ax.set_title('Top 10 Music Styles Distribution')
if save_path:
fig.savefig(save_path, bbox_inches='tight')
return fig
整合分析结果生成PDF报告的关键步骤:
python复制def generate_report(analysis_results, template_file):
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template(template_file)
html = template.render(data=analysis_results)
pdf = HTML(string=html).write_pdf()
with open('music_report.pdf', 'wb') as f:
f.write(pdf)
使用schedule库实现每日自动运行:
python复制def job():
print("开始每日数据采集...")
data = fetch_ranking_data()
analyzed = analyze_data(data)
generate_report(analyzed)
schedule.every().day.at("03:00").do(job)
while True:
schedule.run_pending()
time.sleep(60)
通过实测发现的三个关键优化点:
eval()方法异步请求示例:
python复制async def fetch_song_detail(session, song_id):
url = f"https://music.163.com/api/song/detail?ids={song_id}"
async with session.get(url) as resp:
return await resp.json()
async def fetch_all_details(song_ids):
async with aiohttp.ClientSession() as session:
tasks = [fetch_song_detail(session, sid) for sid in song_ids]
return await asyncio.gather(*tasks)
问题现象:返回的数据为空或结构异常
常见原因:播放量单位不统一(有的用"万"有的用实际数字)
解决方案:
python复制def normalize_play_count(count):
if isinstance(count, str):
if '万' in count:
return float(count.replace('万', '')) * 10000
elif '亿' in count:
return float(count.replace('亿', '')) * 100000000
return float(count)
字体显示异常的解决方法:
python复制plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows
plt.rcParams['font.family'] = 'sans-serif'
目前系统已经稳定运行了三个月,积累了一些值得分享的扩展思路:
实现跨平台分析的关键是设计统一的数据模型:
python复制class BaseMusicItem:
def __init__(self):
self.platform = None
self.song_id = None
self.title = None
self.artist = None
self.rank = None
@classmethod
def from_netease(cls, data):
item = cls()
item.platform = 'netease'
item.song_id = data['id']
# 其他字段映射...
return item
这个项目给我的最大启示是:好的数据分析系统应该像音乐本身一样——既有严谨的结构(如同乐理),又能灵活适应变化(即兴演奏)。特别是在处理网页反爬时,需要保持对数据源的持续观察和快速适应能力。如果你也想尝试类似项目,我的建议是先从小规模数据开始,逐步验证每个环节的可靠性,再扩展到全量采集分析。