Python案例:分享一段能够下载免费歌曲的代码
- 2026-11-07 16:09:03
Python案例:分享一段能够下载免费歌曲的代码
import requestsimport osimport timeimport refrom difflib import SequenceMatcher################################ !! 用户配置区 - 只需修改这里 !!###############################CONFIG = {# 1. 想下载的歌曲信息'SONG_NAME': '千年等一回', # 修改为你想下载的歌曲名'ARTIST_NAME': '鞠婧祎', # 修改为希望的原唱歌手# 2. 平台搜索优先级 (越靠前越先尝试)'PLATFORM_PRIORITY': ['netease', 'qqmusic', 'soda'], # 可选: netease, qqmusic, soda# 3. 歌曲名匹配严格度 (0-1, 越高越严格)'SONG_MATCH_THRESHOLD': 0.6, # 0.6表示至少60%相似度# 4. 文件保存设置'SAVE_DIR': './下载的音乐',# 5. 是否启用调试信息'DEBUG': True}################################ 配置区结束################################ ==================== 通用工具函数 ====================def create_save_dir(path):"""创建保存目录"""if not os.path.exists(path):os.makedirs(path)if CONFIG['DEBUG']:print(f"[系统] 创建目录: {path}")def sanitize_filename(filename):"""清理文件名中的非法字符"""illegal_chars = r'\/:*?"<>|'for char in illegal_chars:filename = filename.replace(char, '_')filename = filename.replace('\n', '_').replace('\r', '_')if len(filename) > 100:name, ext = os.path.splitext(filename)filename = name[:80] + "..." + extreturn filenamedef clean_song_name(song_name):"""清理歌曲名,移除括号内容、空格等,用于匹配"""# 移除括号及括号内的内容song_name = re.sub(r'[\((].*?[\))]', '', song_name)# 移除特殊字符和空格song_name = re.sub(r'[-\s\.\,]', '', song_name)# 转换为小写(可选)song_name = song_name.lower()return song_namedef calculate_similarity(str1, str2):"""计算两个字符串的相似度 (0-1)"""# 先清理两个字符串str1_clean = clean_song_name(str1)str2_clean = clean_song_name(str2)# 如果清理后完全一致,相似度为1.0if str1_clean == str2_clean:return 1.0# 使用difflib计算相似度return SequenceMatcher(None, str1_clean, str2_clean).ratio()def download_audio(url, filepath, headers):"""通用音频下载函数"""try:response = requests.get(url, headers=headers, stream=True, timeout=30)response.raise_for_status()content_type = response.headers.get('Content-Type', '')if not content_type.startswith('audio/') and 'video/' not in content_type:if CONFIG['DEBUG']:print(f"[下载] 警告: 返回内容类型异常: {content_type}")return Falsewith open(filepath, 'wb') as f:for chunk in response.iter_content(chunk_size=8192):if chunk:f.write(chunk)return Trueexcept Exception as e:if CONFIG['DEBUG']:print(f"[下载] 下载失败: {e}")return False# ==================== 平台搜索模块 ====================class NeteaseMusicSearcher:"""网易云音乐搜索器"""@staticmethoddef search_song(song_name, artist_name):"""在网易云搜索歌曲,返回整理后的结果列表"""print(f"[网易云] 正在搜索: {song_name} - {artist_name}")headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36','Referer': 'https://music.163.com/'}try:# 构造搜索URLencoded_keyword = requests.utils.quote(f"{song_name}{artist_name}")url = f"https://music.163.com/api/search/get/web?csrf_token=&s={encoded_keyword}&type=1&offset=0&limit=20"response = requests.get(url, headers=headers, timeout=10)data = response.json()if data['code'] != 200 or not data.get('result', {}).get('songs'):print("[网易云] 搜索无结果")return []songs = data['result']['songs']processed_results = []target_song_clean = clean_song_name(song_name)for song in songs:song_id = song['id']song_title = song['name']primary_artist = song['artists'][0]['name'] if song['artists'] else '未知'# 计算歌曲名相似度song_similarity = calculate_similarity(song_title, song_name)# 判断是否为原唱is_original = (artist_name in primary_artist) or (primary_artist in artist_name)# 构造播放链接play_url = f"https://music.163.com/song/media/outer/url?id={song_id}.mp3"# 计算综合匹配度分数match_score = 0# 歌曲名相似度权重: 60%match_score += song_similarity * 60# 原唱权重: 30%match_score += 30 if is_original else 0# 歌手名匹配权重: 10%match_score += 10 if (artist_name in primary_artist) else 0processed_results.append({'platform': 'netease','id': song_id,'title': song_title,'artist': primary_artist,'is_original': is_original,'song_similarity': song_similarity,'match_score': match_score, # 综合匹配分数'url': play_url,'source': '网易云音乐'})# 按匹配度排序processed_results.sort(key=lambda x: x['match_score'], reverse=True)print(f"[网易云] 找到 {len(processed_results)} 个结果")# 显示前3个最佳匹配for i, result in enumerate(processed_results[:3]):print(f" {i+1}. {result['title']} - {result['artist']} "f"(相似度: {result['song_similarity']:.2f}, 原唱: {result['is_original']})")return processed_resultsexcept Exception as e:print(f"[网易云] 搜索出错: {e}")return []class QQMusicSearcher:"""QQ音乐搜索器 (示例框架)"""@staticmethoddef search_song(song_name, artist_name):"""QQ音乐搜索接口示例"""print(f"[QQ音乐] 正在搜索: {song_name} - {artist_name}")time.sleep(0.5)# 示例数据 - 模拟不同匹配度的结果example_results = [{'platform': 'qqmusic','id': '001','title': f'{song_name}','artist': artist_name,'is_original': True,'song_similarity': 0.95,'match_score': 95,'url': f'https://api.qq.com/song/001.mp3','source': 'QQ音乐'},{'platform': 'qqmusic','id': '002','title': f'{song_name} (Live版)','artist': artist_name,'is_original': True,'song_similarity': 0.85,'match_score': 85,'url': f'https://api.qq.com/song/002.mp3','source': 'QQ音乐'},{'platform': 'qqmusic','id': '003','title': f'其他歌曲','artist': artist_name,'is_original': True,'song_similarity': 0.2,'match_score': 20,'url': f'https://api.qq.com/song/003.mp3','source': 'QQ音乐'}]print(f"[QQ音乐] 找到 {len(example_results)} 个结果 (示例)")for i, result in enumerate(example_results[:3]):print(f" {i+1}. {result['title']} - {result['artist']} "f"(相似度: {result['song_similarity']:.2f}, 原唱: {result['is_original']})")return example_resultsclass SodaMusicSearcher:"""汽水音乐搜索器 (示例框架)"""@staticmethoddef search_song(song_name, artist_name):"""汽水音乐搜索接口示例"""print(f"[汽水音乐] 正在搜索: {song_name} - {artist_name}")time.sleep(0.5)example_results = [{'platform': 'soda','id': 'soda_001','title': f'{song_name}','artist': '翻唱歌手','is_original': False,'song_similarity': 0.90,'match_score': 70, # 不是原唱,分数较低'url': f'https://soda.com/track/soda_001.mp3','source': '汽水音乐'}]print(f"[汽水音乐] 找到 {len(example_results)} 个结果 (示例)")return example_results# ==================== 主逻辑函数 ====================def filter_and_sort_results(results, target_song, target_artist, threshold=0.6):"""过滤和排序搜索结果返回: (qualified_results, unqualified_results)"""qualified = []unqualified = []for result in results:similarity = result.get('song_similarity', 0)# 如果歌曲名相似度低于阈值,放入不合格列表if similarity < threshold:unqualified.append(result)else:qualified.append(result)# 对合格的结果按匹配度分数排序qualified.sort(key=lambda x: x.get('match_score', 0), reverse=True)return qualified, unqualifieddef smart_music_downloader():"""智能多平台音乐下载主函数"""song_name = CONFIG['SONG_NAME']artist_name = CONFIG['ARTIST_NAME']platforms = CONFIG['PLATFORM_PRIORITY']save_dir = CONFIG['SAVE_DIR']threshold = CONFIG['SONG_MATCH_THRESHOLD']print("=" * 60)print(f"🎵 智能音乐下载器启动")print(f"📀 目标歌曲: {song_name}")print(f"🎤 目标歌手: {artist_name}")print(f"🎯 歌曲匹配阈值: {threshold}")print(f"🔄 搜索平台: {', '.join(platforms)}")print("=" * 60)create_save_dir(save_dir)searchers = {'netease': NeteaseMusicSearcher.search_song,'qqmusic': QQMusicSearcher.search_song,'soda': SodaMusicSearcher.search_song}# 第一阶段:在所有平台搜索,优先下载高匹配度的原唱版本print("\n✅ 第一阶段: 搜索并下载最佳匹配版本...")for platform in platforms:if platform not in searchers:print(f"[系统] 跳过未知平台: {platform}")continueprint(f"\n{'='*40}")print(f"尝试平台: {platform.upper()}")print(f"{'='*40}")search_func = searchers[platform]results = search_func(song_name, artist_name)if not results:print(f"[{platform}] 无搜索结果,尝试下一平台")continue# 过滤和排序结果qualified_results, unqualified_results = filter_and_sort_results(results, song_name, artist_name, threshold)print(f"[{platform}] 合格结果: {len(qualified_results)} 个 | "f"不合格结果: {len(unqualified_results)} 个")if not qualified_results:print(f"[{platform}] 无合格结果(相似度<{threshold}),尝试下一平台")continue# 尝试下载合格结果(已按匹配度排序)for i, song_info in enumerate(qualified_results[:5]): # 最多尝试前5个合格结果print(f"\n[{platform}] 尝试 #{i+1}: {song_info['title']} - {song_info['artist']}")print(f" 匹配度: {song_info['match_score']}/100 | "f"相似度: {song_info.get('song_similarity', 0):.2f} | "f"原唱: {song_info['is_original']}")# 生成文件名(包含匹配度信息)match_type = "原唱" if song_info['is_original'] else "翻唱"filename = f"{song_info['title']} - {song_info['artist']} ({match_type}_{song_info['source']}_{song_info['match_score']}).mp3"filename = sanitize_filename(filename)filepath = os.path.join(save_dir, filename)# 如果文件已存在,跳过if os.path.exists(filepath):print(f" 文件已存在,跳过下载")continueheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}success = download_audio(song_info['url'], filepath, headers)if success:print(f"🎉 下载成功! | 平台: {song_info['source']}")print(f"💾 保存路径: {filepath}")print("=" * 60)return Trueelse:print(f"[{platform}] 下载失败,尝试下一版本")# 第二阶段:如果高匹配度版本都失败,尝试放宽条件print("\n⚠️ 高匹配度版本全部失败,启动备选方案...")print("✅ 第二阶段: 尝试所有可用版本(包括低匹配度)...")for platform in platforms:if platform not in searchers:continueprint(f"\n--- 尝试平台: {platform.upper()} (放宽条件) ---")search_func = searchers[platform]results = search_func(song_name, artist_name)if not results:continue# 只按匹配度排序,不进行过滤results.sort(key=lambda x: x.get('match_score', 0), reverse=True)for i, song_info in enumerate(results[:3]): # 尝试前3个similarity = song_info.get('song_similarity', 0)print(f"[{platform}] 尝试 #{i+1}: {song_info['title']} - {song_info['artist']} "f"(相似度: {similarity:.2f}, 原唱: {song_info['is_original']})")match_type = "原唱" if song_info['is_original'] else "翻唱"filename = f"{song_info['title']} - {song_info['artist']} ({match_type}_{song_info['source']}_备选).mp3"filename = sanitize_filename(filename)filepath = os.path.join(save_dir, filename)if os.path.exists(filepath):print(f" 文件已存在,跳过下载")continueheaders = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}success = download_audio(song_info['url'], filepath, headers)if success:print(f"🎉 备选版本下载成功! | 平台: {song_info['source']}")print(f"💾 保存路径: {filepath}")print("=" * 60)return True# 所有尝试都失败print("\n❌ 所有平台和版本尝试均失败")print("\n💡 建议调整策略:")print(f" 1. 降低匹配阈值 (当前: {threshold})")print(f" 2. 检查歌曲名是否正确: '{song_name}'")print(f" 3. 尝试其他平台")print("=" * 60)return False# ==================== 程序入口 ====================if __name__ == "__main__":success = smart_music_downloader()if success:print("✨ 任务完成!")else:print("😔 下载失败")# 等待用户查看结果if os.name == 'nt':os.system("pause")
本文来自网友投稿或网络内容,如有侵犯您的权益请联系我们删除,联系邮箱:wyl860211@qq.com 。