1. 项目概述:音乐平台数据爬取的价值与挑战
最近在做一个音乐数据分析的Side Project,需要批量获取网易云和QQ音乐的歌曲信息。作为国内两大主流音乐平台,它们拥有最全的曲库和用户行为数据,但官方API要么限制严格,要么功能有限。这时候Python爬虫就成了获取数据的利器。
这个项目看似简单,实际暗藏不少技术点:
- 需要处理动态渲染的页面(网易云大量使用AJAX)
- 要绕过反爬机制(QQ音乐有频率检测)
- 得解析加密参数(比如网易云的
params和encSecKey) - 还要注意法律风险(遵守robots.txt和合理爬取频率)
重要提示:爬虫请务必设置合理间隔(建议3-5秒/次),避免对服务器造成压力。我曾因测试时忘记加delay,IP被网易云封了24小时。
2. 技术选型与环境准备
2.1 核心工具链选择
经过对比测试,最终确定的工具组合:
python复制# 网络请求
requests # 基础HTTP库(需配合cookies)
aiohttp # 异步请求(适合大批量抓取)
# 浏览器模拟
selenium # 处理复杂JS渲染
undetected-chromedriver # 绕过Cloudflare等反爬
# 数据解析
BeautifulSoup # HTML解析
lxml # 高性能XML解析
jsonpath # 处理嵌套JSON
# 其他工具
fiddler/Charles # 抓包分析
Chrome DevTools # 调试加密逻辑
2.2 关键环境配置
- Python环境建议使用3.8+版本(兼容性最好)
- 安装依赖时注意:
bash复制# 避免版本冲突的推荐安装方式
pip install requests==2.28.1 beautifulsoup4==4.11.1 selenium==4.8.0
- ChromeDriver版本必须与本地Chrome匹配:
python复制# 自动匹配driver版本的方案
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
3. 网易云音乐爬虫实战
3.1 接口逆向分析
通过抓包发现关键接口:
code复制POST https://music.163.com/weapi/v3/song/detail
需要处理两个加密参数:
params- AES加密的请求参数encSecKey- RSA加密的密钥
加密逻辑位于核心JS文件core.js中,逆向步骤:
- 搜索
window.asrsea(定位加密函数 - 提取AES和RSA的公钥参数
- 用Python复现加密过程:
python复制import hashlib
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
def encrypt_params(text):
# 1. 随机生成16字节字符作为secKey
nonce = '0CoJUm6Qyw8W8jud'
pub_key = '010001'
modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
# 2. AES加密
aes_key = 'F'*16
iv = '0102030405060708'
cipher = AES.new(aes_key.encode(), AES.MODE_CBC, iv.encode())
encrypted = cipher.encrypt(text.encode())
result = base64.b64encode(encrypted).decode()
# 3. RSA加密
rs = pow(int.from_bytes(secKey.encode(), 'big'), int(pub_key, 16), int(modulus, 16))
encSecKey = format(rs, 'x').zfill(256)
return {'params': result, 'encSecKey': encSecKey}
3.2 歌曲信息抓取实现
完整请求示例:
python复制import requests
def get_song_detail(song_id):
url = "https://music.163.com/weapi/v3/song/detail"
data = {
"ids": f"[{song_id}]",
"c": '[{"id":%s}]' % song_id
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://music.163.com/'
}
encrypted = encrypt_params(json.dumps(data))
response = requests.post(url, data=encrypted, headers=headers)
return response.json()
# 示例:获取周杰伦《晴天》信息
print(get_song_detail(186016))
返回数据结构示例:
json复制{
"songs": [{
"name": "晴天",
"id": 186016,
"ar": [{"name": "周杰伦"}],
"al": {"name": "叶惠美"},
"dt": 269000, // 时长(ms)
"publishTime": 1059667200000
}],
"privileges": [{}]
}
4. QQ音乐爬虫方案
4.1 接口鉴权机制
QQ音乐主要采用guid和uin进行身份验证:
guid- 设备唯一标识(可用随机数生成)uin- 用户ID(未登录状态用0)
关键接口示例:
code复制GET https://u.y.qq.com/cgi-bin/musicu.fcg?data={...}
请求参数需要构造comm和req两部分:
python复制params = {
"comm": {
"cv": 4747474,
"ct": 24,
"format": "json",
"uin": 0,
"guid": "1234567890"
},
"req": {
"module": "music.pf_song_detail_svr",
"method": "get_song_detail",
"param": {"song_id": 123456}
}
}
4.2 歌曲信息获取
完整实现代码:
python复制def get_qqmusic_detail(song_id):
base_url = "https://u.y.qq.com/cgi-bin/musicu.fcg"
params = {
"comm": {
"cv": 4747474,
"ct": 24,
"format": "json",
"uin": 0,
"guid": str(random.randint(1000000000, 9999999999))
},
"req": {
"module": "music.pf_song_detail_svr",
"method": "get_song_detail",
"param": {"song_id": song_id}
}
}
response = requests.get(base_url, params={'data': json.dumps(params)})
return response.json()
# 示例:获取《孤勇者》信息
print(get_qqmusic_detail(212877280))
典型响应数据:
json复制{
"code": 0,
"data": {
"track_info": {
"title": "孤勇者",
"singer": [{"name": "陈奕迅"}],
"album": {"name": "英雄联盟:双城之战"},
"time_public": "2021-11-08",
"duration": 180
}
}
}
5. 反爬对抗与优化策略
5.1 常见反爬措施应对
| 反爬类型 | 解决方案 | 实现示例 |
|---|---|---|
| IP限制 | 代理IP轮换 | requests.get(proxies={'https':'ip:port'}) |
| UserAgent检测 | 随机UA | fake_useragent.UserAgent().random |
| 行为指纹 | 随机延迟+鼠标移动模拟 | time.sleep(random.uniform(1,3)) |
| 验证码 | 打码平台/OCR识别 | 使用selenium自动识别简单验证码 |
| 参数签名 | JS逆向复现加密逻辑 | 前文网易云加密案例 |
5.2 性能优化技巧
- 异步采集加速:
python复制import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.json()
async def main(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
return await asyncio.gather(*tasks)
- 断点续爬设计:
python复制class Spider:
def __init__(self):
self.cursor = 0
if os.path.exists('progress.json'):
with open('progress.json') as f:
self.cursor = json.load(f)['last_id']
def save_progress(self):
with open('progress.json', 'w') as f:
json.dump({'last_id': self.cursor}, f)
- 数据去重方案:
python复制from bloom_filter import BloomFilter
bf = BloomFilter(max_elements=1000000, error_rate=0.1)
if song_id not in bf:
bf.add(song_id)
# 处理新数据
6. 法律合规与道德规范
6.1 必须遵守的规则
-
robots.txt检查:
- 网易云:https://music.163.com/robots.txt
- QQ音乐:https://y.qq.com/robots.txt
-
合理使用原则:
- 单IP请求频率≤20次/分钟
- 禁止绕过付费内容限制
- 不抓取用户隐私数据
6.2 数据存储建议
- 仅存储必要字段(避免完整歌曲数据)
- 数据库设计示例:
sql复制CREATE TABLE songs (
id INT PRIMARY KEY,
platform VARCHAR(10),
title VARCHAR(255),
artist VARCHAR(255),
album VARCHAR(255),
duration INT,
publish_date DATE,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
7. 完整项目架构示例
code复制music_spider/
├── core/
│ ├── netease.py # 网易云爬虫
│ ├── qqmusic.py # QQ音乐爬虫
│ └── decrypt.py # 加密算法
├── utils/
│ ├── proxy.py # 代理管理
│ ├── storage.py # 数据存储
│ └── anti_spider.py # 反爬处理
├── config.yaml # 配置文件
└── main.py # 入口文件
典型配置文件config.yaml:
yaml复制request:
interval: 3 # 请求间隔(秒)
timeout: 10 # 超时时间
retry: 3 # 重试次数
proxy:
enable: false # 是否启用代理
pool: [] # 代理列表
storage:
db_uri: sqlite:///data.db
csv_path: ./backup
这个项目最让我头疼的是网易云的参数加密,花了整整两天才逆向成功。后来发现其实有更简单的方法——直接调用他们的WebWorker接口,虽然不太规范但确实有效。爬虫开发就是这样,往往需要不断试错才能找到最优解。
