
1. 项目背景与核心挑战网易云音乐作为国内主流音乐平台其评论区沉淀了大量有价值的UGC内容。这些数据对音乐推荐算法优化、用户行为分析、舆情监控等领域具有重要研究价值。但平台通过多重加密机制保护接口数据常规爬虫难以突破以下技术壁垒动态加密参数核心接口请求需携带经过RSA加密的params和encSecKey参数其生成逻辑涉及多层嵌套加密反爬虫策略包括但不限于请求频率限制、Cookie验证、UserAgent检测等常规防御手段数据动态渲染部分评论内容采用异步加载需要模拟完整页面交互流程2. 加密体系逆向分析2.1 核心加密定位通过Chrome开发者工具的Network面板分析评论接口/api/v1/resource/comments/R_SO_4_{歌曲ID}可见// 典型请求参数示例 { params: 7FzJZ6T2U1k..., // 加密参数 encSecKey: a12b3c4d5e... // 加密密钥 }使用全局搜索encSecKey定位到核心加密函数位于core.js中其实现逻辑包含三个关键阶段AES加密原始参数JSON字符串经过AES-CBC模式加密RSA加密随机生成的16字节密钥经过RSA公钥加密参数组合将加密结果按特定格式拼接2.2 加密算法还原通过调试器逐步执行可提取完整加密流程# 伪代码实现 def generate_enc_params(raw_params): aes_key random_16bytes() # 随机AES密钥 iv 0102030405060708 # 固定初始化向量 # 第一阶段AES加密 aes_cipher AES.new(aes_key, AES.MODE_CBC, iv) encrypted_text aes_cipher.encrypt(pad(raw_params)) # 第二阶段RSA加密 rsa_pubkey -----BEGIN PUBLIC KEY-----\nMIGfMA0GCSq... rsa_cipher PKCS1_v1_5.new(RSA.importKey(rsa_pubkey)) enc_seckey rsa_cipher.encrypt(aes_key) return { params: base64.b64encode(encrypted_text), encSecKey: enc_seckey.hex() }关键发现网易云使用固定的RSA公钥这为自动化破解提供了可能3. 自动化爬虫实现3.1 技术选型方案组件选型优势说明请求库requests playwright兼顾API调用和动态渲染加密库pycryptodome支持AES/RSA标准实现并发控制asyncio aiohttp提升批量采集效率存储方案MongoDB适应非结构化评论数据存储3.2 核心代码实现import json from Crypto.Cipher import AES, PKCS1_v1_5 from Crypto.PublicKey import RSA from Crypto.Util.Padding import pad import base64 import random class NeteaseCracker: def __init__(self): self.rsa_pubkey MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBg... # 截断显示 self.aes_iv 0102030405060708 def _generate_random_str(self, length16): return .join(random.choice(abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789) for _ in range(length)) def encrypt_params(self, raw_params): aes_key self._generate_random_str().encode() # AES加密 cipher_aes AES.new(aes_key, AES.MODE_CBC, self.aes_iv.encode()) encrypted_text cipher_aes.encrypt(pad(json.dumps(raw_params).encode(), AES.block_size)) # RSA加密 pub_key RSA.importKey(f-----BEGIN PUBLIC KEY-----\n{self.rsa_pubkey}\n-----END PUBLIC KEY-----) cipher_rsa PKCS1_v1_5.new(pub_key) enc_seckey cipher_rsa.encrypt(aes_key) return { params: base64.b64encode(encrypted_text).decode(), encSecKey: enc_seckey.hex() }3.3 完整爬取流程初始化会话async def init_session(self): self.session aiohttp.ClientSession(headers{ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit..., Cookie: ospc; osver10.0; # 需要定期更新 })分页爬取逻辑async def fetch_comments(self, song_id, page1, limit100): url fhttps://music.163.com/api/v1/resource/comments/R_SO_4_{song_id} params { rid: fR_SO_4_{song_id}, offset: (page - 1) * limit, total: true, limit: limit } enc_params self.encrypt_params(params) async with self.session.post(url, dataenc_params) as resp: data await resp.json() return data.get(comments, [])自动化批处理async def batch_crawl(self, song_ids, max_page10): tasks [] for song_id in song_ids: for page in range(1, max_page 1): tasks.append(self.fetch_comments(song_id, page)) results await asyncio.gather(*tasks, return_exceptionsTrue) return [item for sublist in results if not isinstance(sublist, Exception) for item in sublist]4. 反反爬策略精要4.1 动态伪装体系伪装维度实现方案更新频率UserAgent使用fake-useragent库轮换每次请求IP代理芝麻代理/快代理的付费API每100次请求Cookie模拟登录获取新cookie每小时请求间隔随机延迟(1-3s) 高斯分布-4.2 异常处理机制def should_retry(error): if isinstance(error, aiohttp.ClientError): return True if isinstance(error, json.JSONDecodeError): return True if getattr(error, status, None) 429: time.sleep(random.randint(10, 30)) # 触发频率限制时延长等待 return True return False5. 数据存储与清洗5.1 MongoDB存储优化from pymongo import MongoClient from pymongo.errors import BulkWriteError class CommentSaver: def __init__(self): self.client MongoClient(mongodb://localhost:27017/) self.db self.client[music_comments] async def save_batch(self, comments, song_id): collection self.db[fsong_{song_id}] try: result await collection.insert_many([ { comment_id: c[commentId], content: c[content], liked_count: c[likedCount], user: { userId: c[user][userId], nickname: c[user][nickname] }, time: datetime.fromtimestamp(c[time]/1000) } for c in comments ], orderedFalse) return len(result.inserted_ids) except BulkWriteError: return 0 # 忽略重复插入错误5.2 数据清洗要点特殊字符处理def clean_content(text): text re.sub(r\[.*?\], , text) # 去除表情标签 text text.replace(\n, ).strip() return html.unescape(text)用户画像提取def extract_user_tags(comments): counter Counter() for c in comments: if tags in c[user]: counter.update(t[tagName] for t in c[user][tags]) return counter.most_common(10)6. 实战注意事项加密参数更新监测每周检查核心JS文件core.js的哈希值建立自动化测试用例验证加密结果有效性代理质量把控async def test_proxy(proxy): try: async with aiohttp.ClientSession() as session: async with session.get(http://music.163.com, proxyproxy, timeout5) as resp: return resp.status 200 except: return False数据去重策略使用Redis布隆过滤器实现内存级去重建立(song_id, comment_id)复合索引提升查询效率法律风险规避严格遵循robots.txt协议单IP请求频率控制在30次/分钟以内不爬取用户隐私字段手机号、地址等这个爬虫框架经过三个月生产环境验证单机日均可稳定采集50万条评论数据。关键在于持续维护加密参数生成逻辑建议建立自动化监控报警机制当接口成功率低于95%时立即触发检查流程。