【python爬虫】酷狗音乐爬取

打印 上一主题 下一主题

主题 1024|帖子 1024|积分 3072

本次爬取的音乐仅有1分钟试听,完整音乐需要下载客户端
  一、 初步分析

登陆酷狗音乐后随机选取一首歌,在哀求里发现一段mp3文件,复制网址,确实是我们需要的url。

复制音频的名字,搜索找到发起哀求的网址,发现是在songinfo里

查看参数和哀求头,刷新一次,查看是否有哪些参数是变化的。可以发现图中两次哀求的这些参数都差别,接下来就寻找这些参数的天生方式,参数clienttime为时间戳,那么就只需要找到signature的天生方式就可以了:

二、分析参数signature

1. 分析过程

搜索参数signature,并在可能天生的位置打上断点,然后刷新网页

网页断在了此处,可以望见参数signature跟函数d与数组s有关。
   补充:如果s的长度不为13,需要放行一下,点击这个按钮

  

查看函数d的定义

发现函数内部没有wordsToBytes函数和函数i的相关定义,那么打下断点,查看具体函数的位置

得到wordsToBytes函数的具体定义

得到i函数的具体定义

i函数里没有r.stringToBytes(t)的相关定义,继续打下断点

找到r.stringToBytes(t)的相关定义

接着查看s的内容:在console里查看s的内容,发现s的值跟之前哀求的参数类似。而且下标为0和下标为12的值跟u的值雷同

往上查找u的定义,发现u的值是固定的

2. 代码实现

那么就开始实现天生signature的代码(以下为JavaScript代码
  1. function bytesToWords(t) {
  2.     for (var n = [], r = 0, e = 0; r < t.length; r++,
  3.         e += 8)
  4.         n[e >>> 5] |= t[r] << 24 - e % 32;
  5.     return n
  6. }
  7. function rotl(t, n) {
  8.     return t << n | t >>> 32 - n
  9. }
  10. function endian(t) {
  11.     if (t.constructor == Number)
  12.         return 16711935 & rotl(t, 8) | 4278255360 & rotl(t, 24);
  13.     for (var n = 0; n < t.length; n++)
  14.         t[n] = endian(t[n]);
  15.     return t
  16. }
  17. function i(t, c) {
  18.     var l = {
  19.         utf8: {
  20.             stringToBytes: function (t) {
  21.                 return l.bin.stringToBytes(unescape(encodeURIComponent(t)))
  22.             },
  23.             bytesToString: function (t) {
  24.                 return decodeURIComponent(escape(l.bin.bytesToString(t)))
  25.             }
  26.         },
  27.         bin: {
  28.             stringToBytes: function (t) {
  29.                 for (var n = [], r = 0; r < t.length; r++)
  30.                     n.push(255 & t.charCodeAt(r));
  31.                 return n
  32.             },
  33.             bytesToString: function (t) {
  34.                 for (var n = [], r = 0; r < t.length; r++)
  35.                     n.push(String.fromCharCode(t[r]));
  36.                 return n.join("")
  37.             }
  38.         }
  39.     };
  40.     i._ff = function (t, n, r, e, o, i, c) {
  41.         var s = t + (n & r | ~n & e) + (o >>> 0) + c;
  42.         return (s << i | s >>> 32 - i) + n
  43.     },
  44.         i._gg = function (t, n, r, e, o, i, c) {
  45.             var s = t + (n & e | r & ~e) + (o >>> 0) + c;
  46.             return (s << i | s >>> 32 - i) + n
  47.         },
  48.         i._hh = function (t, n, r, e, o, i, c) {
  49.             var s = t + (n ^ r ^ e) + (o >>> 0) + c;
  50.             return (s << i | s >>> 32 - i) + n
  51.         },
  52.         i._ii = function (t, n, r, e, o, i, c) {
  53.             var s = t + (r ^ (n | ~e)) + (o >>> 0) + c;
  54.             return (s << i | s >>> 32 - i) + n
  55.         };
  56.     t.constructor == String ? t = c && "binary" === c.encoding ? o.stringToBytes(t) : l.utf8.stringToBytes(t) : e(t) ? t = Array.prototype.slice.call(t, 0) : Array.isArray(t) || (t = t.toString());
  57.     for (var s = bytesToWords(t), a = 8 * t.length, l = 1732584193, u = -271733879, f = -1732584194, d = 271733878, g = 0; g < s.length; g++)
  58.         s[g] = 16711935 & (s[g] << 8 | s[g] >>> 24) | 4278255360 & (s[g] << 24 | s[g] >>> 8);
  59.     s[a >>> 5] |= 128 << a % 32,
  60.         s[14 + (a + 64 >>> 9 << 4)] = a;
  61.     for (var b = i._ff, p = i._gg, h = i._hh, m = i._ii, g = 0; g < s.length; g += 16) {
  62.         var y = l
  63.             , j = u
  64.             , S = f
  65.             , v = d;
  66.         u = m(u = m(u = m(u = m(u = h(u = h(u = h(u = h(u = p(u = p(u = p(u = p(u = b(u = b(u = b(u = b(u, f = b(f, d = b(d, l = b(l, u, f, d, s[g + 0], 7, -680876936), u, f, s[g + 1], 12, -389564586), l, u, s[g + 2], 17, 606105819), d, l, s[g + 3], 22, -1044525330), f = b(f, d = b(d, l = b(l, u, f, d, s[g + 4], 7, -176418897), u, f, s[g + 5], 12, 1200080426), l, u, s[g + 6], 17, -1473231341), d, l, s[g + 7], 22, -45705983), f = b(f, d = b(d, l = b(l, u, f, d, s[g + 8], 7, 1770035416), u, f, s[g + 9], 12, -1958414417), l, u, s[g + 10], 17, -42063), d, l, s[g + 11], 22, -1990404162), f = b(f, d = b(d, l = b(l, u, f, d, s[g + 12], 7, 1804603682), u, f, s[g + 13], 12, -40341101), l, u, s[g + 14], 17, -1502002290), d, l, s[g + 15], 22, 1236535329), f = p(f, d = p(d, l = p(l, u, f, d, s[g + 1], 5, -165796510), u, f, s[g + 6], 9, -1069501632), l, u, s[g + 11], 14, 643717713), d, l, s[g + 0], 20, -373897302), f = p(f, d = p(d, l = p(l, u, f, d, s[g + 5], 5, -701558691), u, f, s[g + 10], 9, 38016083), l, u, s[g + 15], 14, -660478335), d, l, s[g + 4], 20, -405537848), f = p(f, d = p(d, l = p(l, u, f, d, s[g + 9], 5, 568446438), u, f, s[g + 14], 9, -1019803690), l, u, s[g + 3], 14, -187363961), d, l, s[g + 8], 20, 1163531501), f = p(f, d = p(d, l = p(l, u, f, d, s[g + 13], 5, -1444681467), u, f, s[g + 2], 9, -51403784), l, u, s[g + 7], 14, 1735328473), d, l, s[g + 12], 20, -1926607734), f = h(f, d = h(d, l = h(l, u, f, d, s[g + 5], 4, -378558), u, f, s[g + 8], 11, -2022574463), l, u, s[g + 11], 16, 1839030562), d, l, s[g + 14], 23, -35309556), f = h(f, d = h(d, l = h(l, u, f, d, s[g + 1], 4, -1530992060), u, f, s[g + 4], 11, 1272893353), l, u, s[g + 7], 16, -155497632), d, l, s[g + 10], 23, -1094730640), f = h(f, d = h(d, l = h(l, u, f, d, s[g + 13], 4, 681279174), u, f, s[g + 0], 11, -358537222), l, u, s[g + 3], 16, -722521979), d, l, s[g + 6], 23, 76029189), f = h(f, d = h(d, l = h(l, u, f, d, s[g + 9], 4, -640364487), u, f, s[g + 12], 11, -421815835), l, u, s[g + 15], 16, 530742520), d, l, s[g + 2], 23, -995338651), f = m(f, d = m(d, l = m(l, u, f, d, s[g + 0], 6, -198630844), u, f, s[g + 7], 10, 1126891415), l, u, s[g + 14], 15, -1416354905), d, l, s[g + 5], 21, -57434055), f = m(f, d = m(d, l = m(l, u, f, d, s[g + 12], 6, 1700485571), u, f, s[g + 3], 10, -1894986606), l, u, s[g + 10], 15, -1051523), d, l, s[g + 1], 21, -2054922799), f = m(f, d = m(d, l = m(l, u, f, d, s[g + 8], 6, 1873313359), u, f, s[g + 15], 10, -30611744), l, u, s[g + 6], 15, -1560198380), d, l, s[g + 13], 21, 1309151649), f = m(f, d = m(d, l = m(l, u, f, d, s[g + 4], 6, -145523070), u, f, s[g + 11], 10, -1120210379), l, u, s[g + 2], 15, 718787259), d, l, s[g + 9], 21, -343485551),
  67.             l = l + y >>> 0,
  68.             u = u + j >>> 0,
  69.             f = f + S >>> 0,
  70.             d = d + v >>> 0
  71.     }
  72.     return endian([l, u, f, d])
  73. }
  74. function wordsToBytes(t) {
  75.     for (var n = [], r = 0; r < 32 * t.length; r += 8)
  76.         n.push(t[r >>> 5] >>> 24 - r % 32 & 255);
  77.     return n
  78. }
  79. function bytesToHex(t) {
  80.     for (var n = [], r = 0; r < t.length; r++)
  81.         n.push((t[r] >>> 4).toString(16)),
  82.             n.push((15 & t[r]).toString(16));
  83.     return n.join("")
  84. }
  85. function d(t, r) {
  86.     if (void 0 === t || null === t)
  87.         throw new Error("Illegal argument " + t);
  88.     var e = wordsToBytes(i(t, r));
  89.     return r && r.asBytes ? e : r && r.asString ? o.bytesToString(e) : bytesToHex(e)
  90. }
  91. function getsianature() {
  92.     var s = [
  93.         "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt",
  94.         "appid=1014",
  95.         "clienttime=1741411989613",
  96.         "clientver=20000",
  97.         "dfid=3Mm61k0WDxvm033Epz2worRG",
  98.         "encode_album_audio_id=j410q60",
  99.         "mid=70789bebe63fb74c52e4a911853f5450",
  100.         "platid=4",
  101.         "srcappid=2919",
  102.         "token=cbfe2e174e4b97fd6aca35682cdba3d2b431c4ed95e2dbd1779e37a7975b672c",
  103.         "userid=2307902397",
  104.         "uuid=70789bebe63fb74c52e4a911853f5450",
  105.         "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt"
  106.     ];
  107.     return d(s.join(""))
  108. }
  109. console.log(getsianature());
复制代码
经过一系列调试,可以发现天生的结果与欣赏器天生的值一样,那么天生signature的代码就没问题了。

将一些会变的值改为变量,可以看到参数s的值里只有clienttime的值是会变的,因此修改上述代码中getsianature函数,将参数s的值放在python代码中,getsianature函数改完如下图所示:
  1. function getsianature(s) {
  2.     return d(s.join(""))
  3. }
复制代码
三、获取多首歌

1. 分析过程

点击差别的歌,可以发现每首歌的参数encode_album_audio_id都差别,因此需要获取encode_album_audio_id

搜索歌名,找到歌曲的id

接着查看哀求参数,同样有一个signature参数,刷新多次网页发现signature参数会变化,那么重复之前分析signature的步骤

打下断点,查看停止的位置,参数s有所变化

打印s,查看s的内容,除此之外没有变化,那么就相沿先前的代码

2. 代码实现

完整代码如下,留意这里调用了JavaScript代码,需要安装PyExecJS模块:pip install PyExecJS -i https://pypi.tuna.tsinghua.edu.cn/simple。
本代码中JavaScript文件名为kugou.js,JavaScript代码在参数signature的分析中有写到,以下为python代码:
  1. import json
  2. import re
  3. import time
  4. import requests
  5. import execjs
  6. class kugou_music:
  7.     def __init__(self):
  8.         self.headers = {
  9.             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
  10.             'Referer': 'https://www.kugou.com/',
  11.         }
  12.     def get_signature(self, s):
  13.         with open("kugou.js", "r", encoding="utf-8") as f:
  14.             js = f.read()
  15.         ctx = execjs.compile(js)
  16.         signature = ctx.call("getsianature", s)
  17.         return signature
  18.     def get_one_song_url(self, audio_id):
  19.         timestamp = str(int(time.time() * 1000))
  20.         s = [
  21.             "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt",
  22.             "appid=1014",
  23.             f"clienttime={timestamp}",
  24.             "clientver=20000",
  25.             "dfid=3Mm61k0WDxvm033Epz2worRG",
  26.             f"encode_album_audio_id={audio_id}",
  27.             "mid=70789bebe63fb74c52e4a911853f5450",
  28.             "platid=4",
  29.             "srcappid=2919",
  30.             "token=cbfe2e174e4b97fd6aca35682cdba3d2b431c4ed95e2dbd1779e37a7975b672c",
  31.             "userid=2307902397",
  32.             "uuid=70789bebe63fb74c52e4a911853f5450",
  33.             "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt"
  34.         ]
  35.         signature = self.get_signature(s)
  36.         params = {
  37.             'srcappid': '2919',
  38.             'clientver': '20000',
  39.             'clienttime': timestamp,
  40.             'mid': '70789bebe63fb74c52e4a911853f5450',
  41.             'uuid': '70789bebe63fb74c52e4a911853f5450',
  42.             'dfid': '3Mm61k0WDxvm033Epz2worRG',
  43.             'appid': '1014',
  44.             'platid': '4',
  45.             'encode_album_audio_id': audio_id,
  46.             'token': 'cbfe2e174e4b97fd6aca35682cdba3d2b431c4ed95e2dbd1779e37a7975b672c',
  47.             'userid': '2307902397',
  48.             'signature': signature
  49.         }
  50.         one_song_url = 'https://wwwapi.kugou.com/play/songinfo'
  51.         response = requests.get(one_song_url, headers=self.headers, params=params)
  52.         song_url = response.json()['data']['play_url']
  53.         return song_url
  54.     def get_signal_music(self, audio_id, audio_name):
  55.         song_url = self.get_one_song_url(audio_id)
  56.         response = requests.get(song_url, headers=self.headers)
  57.         with open(f'{audio_name}.mp3', 'wb') as f:
  58.             f.write(response.content)
  59.             print(f'{audio_name}.mp3下载完成')
  60.     def get_song_id(self,keyword):
  61.         timestamp = str(int(time.time() * 1000))
  62.         s = [
  63.             "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt",
  64.             "appid=1014",
  65.             "bitrate=0",
  66.             "callback=callback123",
  67.             f"clienttime={timestamp}",
  68.             "clientver=1000",
  69.             "dfid=3Mm61k0WDxvm033Epz2worRG",
  70.             "filter=10",
  71.             "inputtype=0",
  72.             "iscorrection=1",
  73.             "isfuzzy=0",
  74.             f"keyword={keyword}",
  75.             "mid=70789bebe63fb74c52e4a911853f5450",
  76.             "page=1",
  77.             "pagesize=30",
  78.             "platform=WebFilter",
  79.             "privilege_filter=0",
  80.             "srcappid=2919",
  81.             "token=cbfe2e174e4b97fd6aca35682cdba3d2b431c4ed95e2dbd1779e37a7975b672c",
  82.             "userid=2307902397",
  83.             "uuid=70789bebe63fb74c52e4a911853f5450",
  84.             "NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt"
  85.         ]
  86.         signature = self.get_signature(s)
  87.         params = {
  88.             'callback': 'callback123',
  89.             'srcappid': '2919',
  90.             'clientver': '1000',
  91.             'clienttime': timestamp,
  92.             'mid': '70789bebe63fb74c52e4a911853f5450',
  93.             'uuid': '70789bebe63fb74c52e4a911853f5450',
  94.             'dfid': '3Mm61k0WDxvm033Epz2worRG',
  95.             'keyword': keyword,
  96.             'page': '1',
  97.             'pagesize': '30',
  98.             'bitrate': '0',
  99.             'isfuzzy': '0',
  100.             'inputtype': '0',
  101.             'platform': 'WebFilter',
  102.             'userid': '2307902397',
  103.             'iscorrection': '1',
  104.             'privilege_filter': '0',
  105.             'filter': '10',
  106.             'token': 'cbfe2e174e4b97fd6aca35682cdba3d2b431c4ed95e2dbd1779e37a7975b672c',
  107.             'appid': '1014',
  108.             'signature': signature
  109.         }
  110.         song_id_url = 'https://complexsearchretry.kugou.com/v2/search/song'
  111.         response = requests.get(song_id_url, headers=self.headers, params=params, verify=False)
  112.         temp = re.findall(r'callback123(.*)', response.text)[0][1:-1]
  113.         temp = json.loads(temp)
  114.         song = temp['data']['lists']
  115.         return song
  116.     def get_all_song(self,keyword):
  117.         song = self.get_song_id(keyword)
  118.         for i in song:
  119.             song_id = i.get('EMixSongID')
  120.             song_name = i.get('FileName')
  121.             # print(song_name, song_id)
  122.             try:
  123.                 self.get_signal_music(song_id, song_name)
  124.             except Exception as e:
  125.                 print(f'{song_name}下载失败:', e)
  126. if __name__ == '__main__':
  127.     kugou = kugou_music()
  128.     # kugou.get_signal_music('j410q60')
  129.     keyword='周杰伦'
  130.     kugou.get_all_song(keyword)
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

石小疯

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表