一、接口分析:找准评论数据请求入口

在抓取网易云音乐歌曲评论时,我们不难发现,页面并不会随着翻页刷新跳转,评论的加载属于异步AJAX请求。这类接口有一个明显特点:接口地址固定不变,分页切换完全依靠请求载荷中的参数控制,无需更换请求URL,这也是当下主流网站动态加载数据的常见实现方式。想要顺利拿到完整的评论数据,第一步就是吃透接口的加密规则,破解前端加密参数,还原完整的请求逻辑,才能精准构造出平台认可的合法请求载荷,顺利打通数据抓取的关键环节。

二、核心加密JS代码还原

网易云音乐为了防止数据被随意爬取,对评论接口做了严格的参数加密处理,直接请求接口会被拒绝访问。想要攻克这个难点,我们需要先还原前端的加密逻辑,拿到生成params和encSecKey这两个核心校验参数的代码,这也是整个爬虫能否跑通的关键所在。我们通过分析网页源码,还原出完整的加密函数,直接复用这套代码,即可完成加密参数的生成,省去手动逆向分析的繁琐步骤,具体代码如下:

function a(a) {
    var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
    for (d = 0; a > d; d += 1)
        e = Math.random() * b.length,
        e = Math.floor(e),
        c += b.charAt(e);
    return c
}
function b(a, b) {
    var c = CryptoJS.enc.Utf8.parse(b)
      , d = CryptoJS.enc.Utf8.parse("0102030405060708")
      , e = CryptoJS.enc.Utf8.parse(a)
      , f = CryptoJS.AES.encrypt(e, c, {
        iv: d,
        mode: CryptoJS.mode.CBC
    });
    return f.toString()
}
function c(a, b, c) {
    var d, e;
    return setMaxDigits(131),
    d = new RSAKeyPair(b,"",c),
    e = encryptedString(d, a)
}
function d(d, e, f, g) {
    var h = {}
      , i = a(16);
    return h.encText = b(d, g),
    h.encText = b(h.encText, i),
    h.encSecKey = c(i, e, f),
    h
}
function e(a, b, d, e) {
    var f = {};
    return f.encText = c(a + e, b, d),
    f
}
window.asrsea = d,
window.ecnonasr = e

// 构造请求参数
var pageNo = '1'
var str = `{"rid":"R_SO_4_2682113703","threadId":"R_SO_4_2682113703","pageNo":"${pageNo}","pageSize":"20","cursor":"-1","offset":"0","orderType":"1","csrf_token":"df9a3fb556b47931eeec5c5eb9faac9b"}`
var bVA3x = window.asrsea(
    str,
    '010001',
    '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7',
    '0CoJUm6Qyw8W8jud'
);

// 生成最终请求载荷
var e4i = {
    params: bVA3x['encText'],
    encSecKey: bVA3x['encSecKey']
}

console.log(e4i);

三、Python爬虫代码实现

搞定前端加密逻辑之后,我们借助Python的requests库模拟浏览器发送请求,把JS脚本生成的加密参数,直接放入请求的data字段中作为载荷,再搭配完整的请求头和Cookie信息,伪装成正常的网页访问,就能顺利通过平台的校验,调用接口并获取返回的评论JSON数据。完整的Python代码如下,这套代码适配固定接口地址,后续想要翻页抓取,只需要修改对应分页参数即可,上手十分简单:

import requests

# 请求Cookie,维持登录状态
cookies = {
    'vinfo_n_f_l_n3': '3916ff44beb0c078.1.0.1757143646311.0.1757143808916',
    '_ga': 'GA1.1.303110697.1757770466',
    '_clck': '1l10bbd%5E2%5Efza%5E0%5E2082',
    '_ga_C6TGHFPQ1H': 'GS2.1.s1757770465$o1$g1$t1757770693$j60$l0$h0',
    '_iuqxldmzr_': '32',
    '_ntes_nnid': '1ee47c1a495259c0598a10bf662afb17,1757998978281',
    '_ntes_nuid': '1ee47c1a495259c0598a10bf662afb17',
    'NMTID': '00Onmu3maG4d-bidkIqvMR7jmYMoK0AAAGZUOeSwA',
    'WEVNSM': '1.0.0',
    'WNMCID': 'ixgtne.1757998979571.01.0',
    'WM_TID': 'xSejT%2BxV169FUVUARFLC0mdBuTqCwgVE',
    'sDeviceId': 'YD-QZLe%2FrgGqQ1ARxURVUaHgnYMn5zRcAzJ',
    'ntes_utid': 'tid._.GaFvf4nQEeZFB1EBBAKSwiZYi9jUcF2I._.0',
    '__snaker__id': 'QPd9674UKVC6UyD0',
    'ntes_kaola_ad': '1',
    '__csrf': 'df9a3fb556b47931eeec5c5eb9faac9b',
    'WM_NI': 'kK23WUdxhv5GPG3fEMO2OvOADLnrSQX%2FI1qKL%2FiF6qWmCvjqR79%2By3%2FnAONLuYrt3o5WWIMJovb0NQiglc5XxIdE0aJCrrxWNX8vxbZsmkjBieN6SCxdQu3Qy0XgFxPlckE%3D',
    'WM_NIKE': '9ca17ae2e6ffcda170e2e6eeb3d760afe9bba2c240b4e78ab7d45b979e9a87d2728696c0b4fb7daba985b5fb2af0fea7c3b92aa995fed4d65fa8a8a897b84d9499fd87c8478dee8197ce4b8894a293d25b9cf187d2f44190f1a7a7b57b95b38d97e97b92e9f7ccfc39edb28fb0cd48b787be95b265f78999aad042adaaaed1cd5babb9a484b854bc97aca6b36b8a9dbe93fb5d8e88f897fb6a9aada38ce94aabbb82d7d249bcbc9f96b25bbbf09888c57494b4ad8cc837e2a3',
    'gdxidpyhxdE': 'Lr%5Cm7yjN0baYEslbOj70BUDx%2BaSZjyognN6TZ1qp4uNlg%2B7uwWfuXHxCZQ0koz%5CM6QC8Z87kvmw7Vq23kZMRAGPjo3tT0Za37oGUh6v95wvQOX8d9k9dPeixk7XVIMVownl8i2VoBNvmZKk2%2F4HeHBBnUv%2B5X5Qhd5gsHVfq52vpWnTA%3A1761309901295',
    '__csrf': '2e43ce8440e8205e4ff136f3bee8a999',
    'MUSIC_U': '00A6A69BA388EFF9FF603463A34052FA657123138702A5775E8A75E2BBE8E49E0814AACC357E7005C3F6BE0B9B8CCE2F34FA0F52FF11F21A892879D4B86B204FF69C20FEAA5239B90F3688D110A90F195A07B6432F29A3FD654CD91A34291DEE2DF4304B30FE2620A8E985679290983FD5F10427790F8AF4D8A6B515D20B37FB4BAFAC033F3D1F4CC49E66AA60E38F4C52704336694DB87DAAA5AAB16077972A982A24FE077C5C7CCE452DEB5BFFB3B267B4D5F5CD5414B3B14FAC60DA9EF8FEC0FF31B19D04F09263BF9BF643A46A14354CEEADEA26BB1C12CDEE4C4E9B6279BE90B82A2AD96682386EABA636B26A7CF1802D226CC46DA59C41F65C836BEE9D930B229DCC8CE609BCFF50DD0D52E40E89829B5D8AC419CDD30FD7A046422D0F16BD8DE34455B8D0DA9A369DD41E6C6394EDB2AF078FBBE219C9659033549885544E0F77B51B64AEE79965065357C4FE7222DC8CC181E7602CBF88AD2760EC257045DA8269508775488D84B8CB45F3173C694A5314567ACD1353A3290C9C979E9BF30B78FC78F5577F1F3CCDBF79B9823A6',
    'JSESSIONID-WYYY': 'nkA34tOD5jlNrlto0vr%5CFq%5C1QSBOsUNotpH5B9uX3DK%5C1yO0tjI4pe%2Bkvi3rPSugdDD4DbpD%2Bpq%5CJ7FJ%5CmRrTorD3ZNHTVSehKch8St0xVd%2BXvBdVmGwmz09dmwVbyeiFRjy%2F8sCsqQivwfSPvGFBnE5khdkmCIn8HrJOnaSYSUDIJxa%3A1761313070855',
}

# 请求头,模拟浏览器访问,规避基础反爬
headers = {
    'accept': '*/*',
    'accept-language': 'zh-CN,zh;q=0.9',
    'cache-control': 'no-cache',
    'content-type': 'application/x-www-form-urlencoded',
    'nm-gcore-status': '1',
    'origin': 'https://music.163.com',
    'pragma': 'no-cache',
    'priority': 'u=1, i',
    'referer': 'https://music.163.com/song?id=2682113703',
    'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'same-origin',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
}

# 接口公共参数
params = {
    'csrf_token': 'df9a3fb556b47931eeec5c5eb9faac9b',
}

# 加密后的请求载荷,替换为JS生成的最新参数即可
data = {
  "params": 'LuinkKIvOA23xCKsaM72y+ok/qhQ/UH7oR1u5yjTzzv3TqaZAitAVLZZ0p93HupMAJpIo9Ik1JsGPj1Gj4CItoARiQrkpCqjT4qa4KHZqFRQV6OnEcCfgidjiaDj/SLasLb38s2qtj+oWtxLclwUIAni4Smc7wZLNISUGfmzOlkG+NOqJ5ORwUdRiqeP1hzcE94NsNi7rHMAQww/XPS09AOcwPxOeGlh0ueQCrTisKhLoiCvBF9OaOW4Y+Lp/U458dSxr855EEDbmK23VGZEv5Kqsrnn78ZIs1f83Apcayv92NgyKwCi0mcH5yykHXaM8W27L8nUlk8dLbMFIv0CcM4rYnSW8ahhTJYSm8HVKVo=',
  "encSecKey": 'c023f645a6815df0d22b792757ed5d414afd3874242432e5b7df4bde40d1113b1c4e1217de5c38e40612c866136321c241719197f9225d86d2bda01151f1921943a9b8a0a3a494793855c4603ce64db8108d1f92b2737bfbfb41cc4696e61a20a0af8d8a216ebc00cd2e5e20bdc76b73a190a615efe31d43e9b9f7102fbb78ee'
}

# 发送POST请求,获取评论数据
response = requests.post(
    'https://music.163.com/weapi/comment/resource/comments/get',
    params=params,
    cookies=cookies,
    headers=headers,
    data=data,
)

# 打印返回的JSON数据,包含评论内容、发布时间等信息
print(response.text)

四、数据解析与分页思路

接口成功响应后,返回的是标准规整的JSON格式数据,里面完整包含了当前页码下的所有评论内容、用户昵称、发布时间、点赞数量、评论楼层等详细信息,数据结构清晰易懂。我们只需要借助Python自带的json库,对返回的数据进行加载和解析,就能精准提取出自己想要的字段,后续还可以做数据保存、筛选、统计等后续处理。

得益于接口固定的特性,分页爬取的逻辑十分简单,只需要修改JS代码中的pageNo参数,重新生成一组加密载荷,再替换Python代码中data里的对应参数,即可循环爬取多页评论,实现批量抓取。整个流程无需改动URL、请求头和Cookie,仅调整分页参数就能实现翻页,新手也能快速上手,轻松掌握动态接口的爬取技巧。

五、版权声明

本文仅做技术交流和学习使用,严格尊重网易云音乐平台版权,所有内容请勿用于商业用途,违者后果自负。

六、进阶学习

本次实战只是爬虫入门级案例,主要带大家熟悉加密接口逆向、AJAX动态数据抓取的基础流程。想要掌握更多网站的加密破解技巧、高阶反爬绕过、高效批量爬取、数据清洗入库等硬核实战干货,持续关注我,后续会更新更多干货满满的爬虫教程和实战项目,带你从零玩转网络爬虫,攻克各类复杂接口。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐