如何从互联网上免费下载歌曲
一、背景
1、是什么?
这是一个使用 Python 编写的自动化工具,能够自动从互联网音乐网站批量下载歌曲。用户只需准备一个包含歌曲名称的文本文件,程序会自动完成搜索、定位、播放、捕获音频链接和下载的全过程。
2、为什么需要?
在日常生活中,我们经常遇到以下场景:
- 制作个人歌单:想收集自己喜欢的多首歌曲,但手动一首首搜索下载非常耗时
- 离线聆听:需要下载歌曲到本地,以便在没有网络的环境下欣赏
- 批量处理需求:当需要下载几十甚至上百首歌曲时,手动操作效率极低
传统的音乐下载方式需要:
- 手动打开浏览器
- 搜索每首歌曲
- 点击搜索结果
- 等待页面加载
- 手动复制链接
- 使用下载工具
这个脚本将这些步骤全部自动化,只需准备歌曲名单,一键完成所有下载任务。
二、原理
1、核心技术栈
这个程序基于以下几个关键技术实现:
| 技术 | 作用 |
|---|---|
| Python | 编程语言基础 |
| Selenium | 浏览器自动化工具 |
| ChromeDriver | Chrome 浏览器控制接口 |
| Requests | HTTP 请求库,用于下载文件 |
2、工作流程
整个下载流程可以分为以下几个阶段:
┌─────────────────────────────────────┐
│ 开始程序 │
└────────────┬────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 1. 读取 list.txt 中的歌曲列表 │
└────────────┬────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 2. 启动 Chrome 浏览器,访问音乐网站 │
└────────────┬────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 3. 对每首歌曲循环执行: │
│ ├── 构建搜索 URL 并访问 │
│ ├── 等待并点击搜索结果 │
│ ├── 等待新窗口打开并切换 │
│ ├── 点击播放按钮触发音频加载 │
│ ├── 捕获页面加载的 OGG 音频链接 │
│ └── 下载音频文件到本地 │
└────────────┬────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 4. 关闭浏览器,程序结束 │
└─────────────────────────────────────┘
3、关键技术点详解
3.1、浏览器自动化(Selenium)
Selenium 是一个用于 Web 应用测试的自动化工具,它可以:
- 自动启动浏览器
- 模拟用户操作(点击、输入、滚动等)
- 等待元素加载
- 切换窗口和标签页
在本程序中,Selenium 负责模拟用户在音乐网站上的所有操作。
3.2、音频链接捕获
这是最巧妙的部分。当播放按钮被点击后,浏览器会开始加载音频资源。程序通过 JavaScript 的 performance.getEntries() 接口获取页面已加载的所有资源,从中筛选出 .ogg 后缀的音频文件链接。
3.3、携带 Cookies 下载
直接使用音频链接下载可能会因为缺少必要的请求头或 Cookies 而被拒绝。程序从浏览器中提取 Cookies,并在下载请求中携带这些凭证,确保下载成功。
3.4、重试机制
考虑到网络不稳定可能导致加载失败,程序内置了重试逻辑:
- 音频链接捕获最多重试 3 次
- 下载请求失败自动重试 3 次
- 超时处理避免程序卡死
三、如何操作
1、环境准备
1.1、 安装 Python
首先确保已安装 Python 3.6 及以上版本。下载地址:https://www.python.org/downloads/
1.2、安装依赖库
打开命令行,执行以下命令安装所需的 Python 库:
bash
pip install selenium requests
1.3、安装 Chrome 浏览器和 ChromeDriver
确保电脑已安装 Google Chrome 浏览器。ChromeDriver 版本需与 Chrome 浏览器版本匹配。
2、文件准备
2.1、创建歌曲列表文件
在项目目录下创建一个名为 list.txt 的文件,每行写入一首歌曲的名称:
歌曲名 1
歌曲名 2
歌曲名 3
例如:
晴天
告白气球
小幸运
2.2、确认下载目录
代码中默认下载到 ./Music/ 文件夹,程序会自动创建该目录。
3、代码详解
下面是对核心代码的逐段解释:
3.1、导入模块
python
import codecs # 用于读写 UTF-8 编码的文件
import os # 用于文件路径操作
import time # 用于延时等待
import requests # 用于 HTTP 下载
from selenium import webdriver # 浏览器自动化
from selenium.webdriver.common.by import By # 元素定位方式
from selenium.webdriver.support.ui import WebDriverWait # 等待工具
from selenium.webdriver.support import expected_conditions as EC # 等待条件
3.2、下载函数核心逻辑
python
def download_single_song(driver, song_name, download_path, original_window):
"""下载单首歌曲的完整流程"""
这个函数接收四个参数:
driver: 浏览器驱动实例song_name: 要下载的歌曲名download_path: 下载保存路径original_window: 原始浏览器窗口句柄(用于切换回来)
3.3、搜索歌曲
python
search_url = f'https://gequba.com/s/{song_name}'
driver.get(search_url)
构建搜索 URL 并访问,这是通过 URL 参数直接跳转到搜索结果页。
3.4、等待并点击搜索结果
python
target_row_selector = "div.row.no-gutters.py-2d5.border-top.align-items-center"
row_element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, target_row_selector))
)
row_element.click()
这里使用了 CSS 选择器来定位搜索结果行。WebDriverWait 会最多等待 10 秒,直到元素可点击。
3.5、切换新窗口
python
WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) > 1)
for handle in driver.window_handles:
if handle != original_window:
new_window = handle
break
driver.switch_to.window(new_window)
点击搜索结果后会打开新窗口,这段代码等待新窗口出现并切换过去。
3.6、捕获音频链接
python
play_btn = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "div.aplayer-button.aplayer-play"))
)
play_btn.click()
time.sleep(2)
entries = driver.execute_script("return window.performance.getEntries().map(e => e.name);")
for url in entries:
if '.ogg' in url:
ogg_url = url
break
点击播放按钮后,通过执行 JavaScript 获取页面加载的所有资源,筛选出音频文件链接。
3.7、下载文件
python
cookies = {c['name']: c['value'] for c in driver.get_cookies()}
headers = {
'User-Agent': 'Mozilla/5.0...',
'Referer': driver.current_url,
'Accept': 'audio/webm,audio/ogg,audio/*;q=0.9'
}
session = requests.Session()
response = session.get(ogg_url, headers=headers, cookies=cookies, stream=True)
with open(file_path, 'wb') as f:
for chunk in response.iter_content(8192):
f.write(chunk)
携带浏览器的 Cookies 和请求头,使用流式下载避免大文件占用过多内存。
3.8、清理和异常处理
python
finally:
if new_window and new_window in driver.window_handles:
driver.switch_to.window(new_window)
driver.close()
driver.switch_to.window(original_window)
无论成功失败,都会关闭新打开的窗口并切换回原始窗口,保证循环能继续执行。
4、运行程序
在命令行中切换到项目目录,运行:
bash
python main.py
5、预期输出
===== 开始下载:晴天 =====
🎵 捕获到 OGG 链接:https://.../xxx.ogg
✅ 下载成功:./Music/晴天.ogg
结果:成功
===== 开始下载:告白气球 =====
🎵 捕获到 OGG 链接:https://.../yyy.ogg
✅ 下载成功:./Music/告白气球.ogg
结果:成功
6、注意事项
- 网络稳定性:下载过程中需要稳定的网络连接
- 网站可用性:目标音乐网站需要可访问且结构未被大幅修改
- 等待时间:程序中设置了 5 秒间隔,避免请求过快
- 文件重复检查:已存在的文件会跳过下载
- 浏览器版本:保持 Chrome 和 ChromeDriver 版本一致
7、常见问题
Q: 提示找不到元素怎么办?
A: 可能是网站结构更新了,需要检查并更新代码中的 CSS 选择器。
Q: 下载失败显示超时?
A: 检查网络连接,或增加代码中的超时时间设置。
Q: 如何修改下载格式?
A: 代码中保存的文件名为 .ogg 格式,如需转换可使用 FFmpeg 等工具。
四、完整代码
python
import codecs
import os
import time
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def download_single_song(driver, song_name, download_path, original_window):
"""
下载单首歌曲,每个步骤捕获超时异常,失败时关闭可能打开的新窗口并返回 False。
"""
new_window = None
try:
# 1. 确保当前在原始窗口(搜索页)
if driver.current_window_handle != original_window:
driver.switch_to.window(original_window)
# 2. 打开歌曲搜索页(请根据实际情况调整URL格式)
search_url = f'https://gequba.com/s/{song_name}'
driver.get(search_url)
# 3. 点击目标搜索结果行
target_row_selector = "div.row.no-gutters.py-2d5.border-top.align-items-center"
try:
row_element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, target_row_selector))
)
row_element.click()
except TimeoutException:
print(f"❌ 歌曲《{song_name}》搜索结果行未找到或不可点击(超时)")
return False
# 4. 等待新窗口出现
try:
WebDriverWait(driver, 10).until(lambda d: len(d.window_handles) > 1)
except TimeoutException:
print(f"❌ 歌曲《{song_name}》点击后未打开新窗口")
return False
# 5. 切换到新窗口
for handle in driver.window_handles:
if handle != original_window:
new_window = handle
break
driver.switch_to.window(new_window)
# 7. 重试捕获 OGG 链接(最多3次,每次刷新)
ogg_url = None
max_retries = 3
for attempt in range(max_retries):
# 刷新后重新点击播放按钮
try:
play_btn = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, "div.aplayer-button.aplayer-play"))
)
play_btn.click()
except:
print(f"⚠️ 刷新后播放按钮仍不可点击,尝试继续...")
time.sleep(2)
driver.refresh()
time.sleep(2)
entries = driver.execute_script("return window.performance.getEntries().map(e => e.name);")
for url in entries:
if '.ogg' in url: # 根据实际后缀调整
ogg_url = url
print(f"🎵 捕获到OGG链接: {ogg_url}")
break
if ogg_url:
break
if not ogg_url:
print(f"❌ 歌曲《{song_name}》无法捕获音频链接(已重试{max_retries}次)")
return False
# 8. 下载文件(携带浏览器cookies和headers)
cookies = {c['name']: c['value'] for c in driver.get_cookies()}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36',
'Referer': driver.current_url,
'Accept': 'audio/webm,audio/ogg,audio/*;q=0.9'
}
session = requests.Session()
retry = Retry(total=3, backoff_factor=1, status_forcelist=[403, 500, 502, 503, 504])
session.mount('https://', HTTPAdapter(max_retries=retry))
try:
response = session.get(ogg_url, headers=headers, cookies=cookies, stream=True, timeout=30)
response.raise_for_status()
file_path = f"{download_path}{song_name}.ogg"
with open(file_path, 'wb') as f:
for chunk in response.iter_content(8192):
f.write(chunk)
print(f"✅ 下载成功: {file_path}")
return True
except requests.exceptions.RequestException as e:
print(f"❌ 歌曲《{song_name}》下载失败: {e}")
return False
except Exception as e:
# 捕获其他未预料的异常(如浏览器崩溃等)
print(f"❌ 歌曲《{song_name}》发生未知错误: {e}")
return False
finally:
# 9. 清理:关闭新窗口并切回原始窗口
if new_window and new_window in driver.window_handles:
try:
driver.switch_to.window(new_window)
driver.close()
except Exception:
pass # 忽略关闭时的异常
driver.switch_to.window(original_window)
# 主程序
if __name__ == "__main__":
songs=[]
with codecs.open("list.txt","r","utf-8") as f:
for line in f.readlines():
songs.append(line.strip())
download_path = "./Music/"
driver = webdriver.Chrome()
driver.get("https://gequba.com") # 打开首页
original_window = driver.current_window_handle
try:
for song_name in songs:
file_path = f"{download_path}{song_name}.ogg"
if os.path.exists(file_path):
continue
print(f"\n===== 开始下载: {song_name} =====")
success = download_single_song(driver, song_name, download_path, original_window)
print(f"结果: {'成功' if success else '失败'}")
time.sleep(5) # 间隔
finally:
driver.quit()