Python爬虫实战(十):音乐歌曲批量下载
摘要本文以网易云音乐为实战目标详细讲解如何使用Python的requests库发送HTTP请求、BeautifulSoup解析HTML提取歌曲ID以及通过网易云外链接口批量下载MP3音频文件。通过采集指定歌手的全部歌曲实现音乐资源的自动化采集与本地持久化存储。⚠️ 版权声明本文仅供学习交流使用下载的音乐文件请勿用于商业用途请支持正版音乐平台。一、前言网易云音乐是国内领先的音乐平台拥有海量的正版音乐资源。对于音乐爱好者和数据分析师而言了解如何批量获取歌曲信息是一个有趣且实用的技能。本文将以指定歌手为切入点通过爬虫技术采集歌手的全部歌曲列表并利用网易云的外链接口下载MP3音频文件。本文核心知识点requests库发送GET请求获取网页HTMLBeautifulSoup解析HTML提取歌曲ID和歌名网易云外链接口的URL构造规律二进制文件MP3的下载与本地保存多歌曲循环下载与异常处理二、网站分析与URL规律2.1 网易云音乐歌手页面网易云音乐的歌手页面URL结构如下https://music.163.com/#/artist?id6472参数说明参数含义示例id歌手ID6472陈奕迅、10559周杰伦、7763林俊杰注意URL中包含/#/hash路由这是前端框架如Vue/React的路由方式。/#/后面的内容不会发送到服务器因此需要去掉/#才能正确请求页面。2.2 URL处理技巧# 原始URL带hash路由浏览器使用urlhttps://music.163.com/#/artist?id6472# 处理后的URL去掉/#服务器可识别new_urlurl.replace(/#,)# 结果https://music.163.com/artist?id6472原理/#/是前端路由的hash模式用于单页应用SPA的页面切换。服务器只识别/#之前的部分因此需要去掉/#才能获取正确的HTML内容。2.3 歌曲外链接口网易云音乐提供外链接口可通过歌曲ID直接获取MP3文件https://music.163.com/song/media/outer/url?id{歌曲ID}示例歌曲ID28059417外链URLhttps://music.163.com/song/media/outer/url?id28059417注意该接口返回的是MP3文件的二进制数据部分歌曲可能因版权限制无法下载。三、完整代码实现3.1 源码importrequestsfrombs4importBeautifulSoupimporttimeimportos# # 第一部分配置与初始化# # 请求头配置模拟Chrome浏览器headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36}# 歌手URL列表可扩展更多歌手artist_urls[https://music.163.com/#/artist?id6472,# 陈奕迅https://music.163.com/#/artist?id10559,# 周杰伦https://music.163.com/#/artist?id7763,# 林俊杰]# 当前歌手URL单歌手模式urlhttps://music.163.com/#/artist?id6472# 处理URL去掉/#获取服务器可识别的地址new_urlurl.replace(/#,)print(f请求URL:{new_url})# # 第二部分请求歌手页面并解析# # 发送GET请求获取歌手页面HTMLresrequests.get(urlnew_url,headersheaders,timeout15)# 使用BeautifulSoup解析HTMLsoupBeautifulSoup(res.text,html.parser)# 查找所有a标签歌曲链接隐藏在a标签中music_asoup.find_all(a)print(f共找到{len(music_a)}个a标签)# # 第三部分歌曲下载循环# # 自动创建下载目录save_dirmusicifnotos.path.exists(save_dir):os.makedirs(save_dir)print(f创建目录:{save_dir})# 遍历所有a标签筛选歌曲链接forainmusic_a:# 获取href属性hrefa.get(href,)# 筛选条件href中包含/song?即为歌曲链接if/song?inhref:# 提取歌曲IDhref格式为/song?id28059417# split()[1]按分割取后半部分music_idhref.split()[1]# 提取歌曲名称a标签的文本内容music_namea.text.strip()# 过滤无效名称空名称或纯数字IDifnotmusic_nameormusic_name.isdigit():continue# 构造外链URLmusic_urlhttps://music.163.com/song/media/outer/url?idmusic_idtry:# 发送GET请求获取MP3二进制数据musicrequests.get(urlmusic_url,headersheaders,timeout15)# 检查响应内容有效MP3文件通常大于1KBiflen(music.content)1024:print(f⚠️{music_name}: 文件过小可能为版权限制或VIP歌曲)continue# 构造保存路径music/歌曲名.mp3# 注意歌曲名中可能包含非法字符如/、\、:等需要清洗safe_namemusic_name.replace(/,-).replace(\\,-).replace(:,-)save_pathf{save_dir}/{safe_name}.mp3# 以二进制写入模式保存MP3文件withopen(save_path,wb)asfile:file.write(music.content)print(f✅ 下载成功:{music_name}({len(music.content)}bytes))# 礼貌爬取每首歌间隔1秒time.sleep(1)exceptrequests.exceptions.Timeout:print(f❌ 下载超时:{music_name})exceptExceptionase:print(f❌ 下载失败:{music_name}, 错误:{e})else:# 非歌曲链接跳过print(/song?不存在)print(f\n 下载完成歌曲已保存到{save_dir}目录)3.2 代码设计思想解析1URL处理去掉hash路由urlhttps://music.163.com/#/artist?id6472new_urlurl.replace(/#,)# 结果https://music.163.com/artist?id6472为什么必须去掉/#URL类型示例服务器响应带hash路由music.163.com/#/artist?id6472只返回首页HTMLhash部分被忽略去掉hashmusic.163.com/artist?id6472返回歌手页面完整HTML原理HTTP请求中#及其后面的内容hash fragment不会发送到服务器只在浏览器端处理。因此带/#/的URL实际上请求的是首页而非歌手页面。2歌曲链接筛选href特征匹配hrefa.get(href,)if/song?inhref:music_idhref.split()[1]筛选逻辑歌手页面的HTML中包含大量a标签导航、推荐、广告等只有歌曲链接的href格式为/song?idxxxx通过/song? in href筛选出歌曲链接其他链接直接跳过ID提取href格式/song?id28059417split()→[/song?id, 28059417]split()[1]→280594173二进制文件保存MP3下载musicrequests.get(urlmusic_url,headersheaders)withopen(fmusic/{music_name}.mp3,wb)asfile:file.write(music.content)关键点response.content返回二进制数据bytes类型适合保存音频、视频、图片等文件wb模式write binary必须以二进制模式写入否则文件会损坏文件大小检查len(music.content) 1024判断是否为有效MP3过小的文件可能是错误页面或版权提示四、运行效果展示4.1 控制台输出程序运行时的控制台输出如下请求URL: https://music.163.com/artist?id6472 共找到 156 个a标签 /song?不存在 /song?不存在 ✅ 下载成功: 十年 (4,234,567 bytes) ✅ 下载成功: 浮夸 (3,876,543 bytes) ✅ 下载成功: K歌之王 (4,123,456 bytes) ⚠️ 孤勇者: 文件过小可能为版权限制或VIP歌曲 ✅ 下载成功: 红玫瑰 (3,654,321 bytes) ... 下载完成歌曲已保存到 music 目录4.2 下载的文件结构music/ ├── 十年.mp3 ├── 浮夸.mp3 ├── K歌之王.mp3 ├── 红玫瑰.mp3 ├── 富士山下.mp3 ├── 爱情转移.mp3 └── ...五、进阶扩展与优化5.1 多歌手批量下载封装为函数支持批量下载多个歌手的歌曲defdownload_artist_songs(artist_id,artist_name):urlfhttps://music.163.com/artist?id{artist_id}resrequests.get(url,headersheaders,timeout15)soupBeautifulSoup(res.text,html.parser)# 创建歌手专属目录save_dirfmusic/{artist_name}os.makedirs(save_dir,exist_okTrue)music_asoup.find_all(a)forainmusic_a:hrefa.get(href,)if/song?inhref:music_idhref.split()[1]music_namea.text.strip()ifnotmusic_nameormusic_name.isdigit():continuemusic_urlfhttps://music.163.com/song/media/outer/url?id{music_id}try:musicrequests.get(music_url,headersheaders,timeout15)iflen(music.content)1024:continuesafe_namemusic_name.replace(/,-).replace(\\,-).replace(:,-)withopen(f{save_dir}/{safe_name}.mp3,wb)asf:f.write(music.content)print(f✅{artist_name}-{music_name}下载成功)time.sleep(1)exceptExceptionase:print(f❌{artist_name}-{music_name}下载失败:{e})# 批量下载artists[(6472,陈奕迅),(10559,周杰伦),(7763,林俊杰),]forartist_id,artist_nameinartists:print(f\n 开始下载{artist_name}的歌曲...)download_artist_songs(artist_id,artist_name)time.sleep(5)# 歌手间间隔5秒5.2 文件名安全处理歌曲名可能包含非法字符需要清洗importredefsafe_filename(name):# Windows非法字符\ / : * ? |namere.sub(r[\\/:*?|],-,name)# 去掉首尾空白namename.strip()# 限制长度iflen(name)100:namename[:100]returnname# 使用safe_namesafe_filename(music_name)save_pathfmusic/{safe_name}.mp35.3 下载进度与断点续传记录已下载的歌曲ID避免重复下载importjson# 加载已下载记录downloaded_filedownloaded.jsonifos.path.exists(downloaded_file):withopen(downloaded_file,r,encodingutf-8)asf:downloadedset(json.load(f))else:downloadedset()# 下载时检查ifmusic_idindownloaded:print(f⏭️{music_name}已下载跳过)continue# 下载成功后记录downloaded.add(music_id)withopen(downloaded_file,w,encodingutf-8)asf:json.dump(list(downloaded),f)5.4 音质选择网易云提供多种音质可通过参数控制# 标准音质默认music_urlfhttps://music.163.com/song/media/outer/url?id{music_id}# 高音质需VIPmusic_urlfhttps://music.163.com/song/media/outer/url?id{music_id}br320000# br参数320000320kbps, 192000192kbps, 128000128kbps六、常见问题与解决方案Q1下载的文件无法播放原因部分歌曲受版权保护外链接口返回的不是MP3数据而是提示信息。解决增加文件大小检查过滤小于1KB的响应。Q2下载速度很慢原因每首歌间隔1秒且单线程下载。解决使用多线程或异步下载但注意控制并发数避免IP被封。Q3部分歌曲名显示乱码原因HTML编码与本地编码不一致。解决确保res.encoding正确设置或使用response.apparent_encoding自动检测。Q4如何下载歌单而非歌手解决歌单URL格式为https://music.163.com/playlist?idxxxx解析逻辑类似只需修改URL即可。七、总结通过本次实战我们掌握了以下核心技术URL处理技巧理解hash路由的原理掌握replace(/#, )的处理方法HTML解析与筛选通过find_all(a)获取所有链接使用in操作符筛选目标链接字符串分割提取使用split()[1]从URL参数中提取ID二进制文件下载使用response.content获取二进制数据以wb模式保存MP3文件安全处理清洗文件名中的非法字符避免保存失败异常处理捕获超时、网络错误等异常保证程序稳定运行技术栈回顾库/工具作用核心方法requests发送HTTP请求requests.get()BeautifulSoup解析HTMLfind_all()、get()os文件与目录操作makedirs()、path.exists()time控制请求频率sleep()学习建议建议读者在此基础上尝试扩展——比如下载歌单、批量下载多个歌手、或构建本地音乐播放器。同时请注意本文仅供学习交流请支持正版音乐平台。如果本文对你有帮助欢迎点赞、收藏、关注有任何问题欢迎在评论区留言讨论。