Python爬虫实战:咪咕音乐歌单数据采集与分析
1. 咪咕音乐歌单数据爬取项目概述最近在做一个音乐推荐系统的原型开发需要大量歌单数据作为训练样本。作为国内主流音乐平台之一咪咕音乐拥有丰富的UGC歌单资源非常适合作为数据来源。本文将分享我从咪咕音乐爬取歌单数据的完整方案包含技术选型、反爬应对策略以及实际采集过程中的经验教训。这个项目主要面向两类读者一是需要音乐数据进行研究分析的数据工程师二是想学习现代网页爬虫技术的开发者。整个过程无需复杂的环境配置使用Python基础工具链即可完成适合有一定编程基础但刚接触爬虫的朋友参考实践。2. 技术方案设计与工具选型2.1 目标数据分析咪咕音乐的歌单页面主要包含以下有价值的数据字段歌单基础信息名称、创建者、播放量、收藏数歌曲列表曲目名称、歌手、专辑用户标签风格、场景、情绪等分类标签相关推荐相似歌单关系网络通过开发者工具分析发现这些数据主要通过两种方式加载首屏渲染的HTML中包含基础元数据滚动加载时通过XHR请求返回JSON格式的歌曲列表2.2 技术栈选择考虑到数据量和复杂度最终采用的技术方案组合请求库Requests aiohttp异步加速解析工具BeautifulSoup json模块反爬应对随机User-Agent 代理IP轮换存储方案MySQL关系型存储便于后续分析提示虽然Scrapy框架更强大但对于这种中等规模的定向采集任务轻量级组合反而更灵活高效。3. 核心爬取流程实现3.1 页面请求与会话维持首先需要模拟浏览器行为建立会话import requests from fake_useragent import UserAgent headers { User-Agent: UserAgent().random, Referer: https://music.migu.cn/ } session requests.Session() session.headers.update(headers)关键技巧每次请求更换User-Agent保持Referer头部的合理性使用会话对象维持cookies3.2 歌单列表抓取策略通过分析页面结构发现歌单列表采用分页加载def get_playlist_page(page1): url fhttps://music.migu.cn/v3/music/playlist?page{page} try: resp session.get(url, timeout10) soup BeautifulSoup(resp.text, html.parser) return [a[href] for a in soup.select(.playlist-item a.title)] except Exception as e: print(fPage {page} error: {str(e)}) return []注意事项控制请求频率建议2-3秒/页实现自动翻页直到获取足够数量的歌单链接记录已爬取页码避免重复3.3 歌单详情解析单个歌单页面的关键数据提取示例def parse_playlist(url): resp session.get(url) soup BeautifulSoup(resp.text, html.parser) # 基础信息提取 info { title: soup.select_one(.playlist-title).text.strip(), creator: soup.select_one(.creator-name).text.strip(), play_count: int(soup.select_one(.play-count).text.replace(次播放,)), song_count: int(soup.select_one(.song-count).text.replace(首,)) } # 歌曲列表获取需处理异步加载 songs [] song_items soup.select(.song-item) for item in song_items: songs.append({ name: item.select_one(.song-name).text.strip(), artist: item.select_one(.artist-name).text.strip(), album: item.select_one(.album-name).text.strip() }) return {**info, songs: songs}4. 反爬机制应对方案4.1 验证码触发条件实测发现以下操作容易触发验证连续请求相同API端点高频访问同一IP异常User-Agent4.2 防御规避策略采用的综合应对方案请求间隔随机化import random import time time.sleep(random.uniform(1.5, 3.5))代理IP池配置proxies { http: http://user:passproxy_ip:port, https: https://user:passproxy_ip:port }请求头动态轮换headers_list [ {User-Agent: Mozilla/5.0 (Windows NT 10.0)}, {User-Agent: AppleWebKit/537.36 (KHTML, like Gecko)}, # 添加更多常见浏览器UA ]5. 数据存储与清洗5.1 数据库设计创建的MySQL表结构CREATE TABLE playlists ( id VARCHAR(32) PRIMARY KEY, title VARCHAR(255), creator VARCHAR(100), play_count INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE songs ( id INT AUTO_INCREMENT PRIMARY KEY, playlist_id VARCHAR(32), name VARCHAR(255), artist VARCHAR(100), album VARCHAR(100), FOREIGN KEY (playlist_id) REFERENCES playlists(id) );5.2 数据质量处理常见问题及解决方案编码问题统一转换为UTF-8缺失字段设置默认值或标记异常重复数据通过MD5校验去重import hashlib def get_md5(text): return hashlib.md5(text.encode(utf-8)).hexdigest()6. 实战经验与优化建议6.1 性能优化技巧异步并发实现import aiohttp import asyncio async def fetch(session, url): async with session.get(url) as response: return await response.text() async def main(urls): async with aiohttp.ClientSession() as session: tasks [fetch(session, url) for url in urls] return await asyncio.gather(*tasks)断点续爬方案记录已成功采集的歌单ID定期保存爬取状态异常时从最后成功点恢复6.2 法律合规提醒严格遵守咪咕音乐的robots.txt规定控制请求频率在合理范围采集数据仅用于个人学习研究不进行大规模商业用途的数据转售7. 扩展应用场景获取到的歌单数据可以用于音乐推荐算法训练用户行为模式分析热门趋势预测音乐知识图谱构建在实际项目中我将这些数据与音频特征分析结合构建了一个基于内容的推荐系统原型。一个有趣的发现是周末创建的歌单平均包含更多欢快节奏的歌曲而工作日晚间的歌单则倾向于舒缓风格。