python爬虫(6)-网易云评论
# -*- coding: utf-8 -*- """ @Time : 2022/3/22 10:33 @Author : Andrew @File : 网易云评论.py """ # 找到未加密的参数 # 想办法把参数加密。过程必须参考网易加密过程 params,encSeckey # 请求网易拿到评论解析 POST # from Crypto.cipher import AES import json from base64 import b64encode import requests from Crypto.Cipher import AES url = "https://music.163.com/weapi/comment/resource/comments/get" # 1.首先在源代码里是没有找到评论的,单纯的请求当前页面源代码无法拿到数据 # 2.通过刷新当前页面,f12抓包,找到了评论是在一个叫get?scf_token=的请求,通过预览发现了评论, # 3.查看其header请求的是上面这个url https://music.163.com/weapi/comment/resource/comments/get,后面的csf_token需要登陆后才有值,这里可以不考虑 # 4.鼠标往下滑 发现了两个参数,params,encSeckey但是被加密的,现在就要考虑解密了 # 5.在edge浏览器中,有一个发起程序,点进去会发现一堆 调用 自下而上的排列 # 6.点击第一个调用,会发现大量的代码,点击左下角的{},可以让代码格式化,此时鼠标点会移动到一个地方,注意鼠标光点所在的函数的返回值参数 # 7.在此处加上断点,刷新当前页面,查看作用域中 返回值参数 里面url是否和上面的一样 # 8.如果不一样,点击上面的暂停按钮(因为加了断点,程序并没有运行完),让程序继续执行,直到找到和上面一样的url # 9.这个时候,查看调用堆栈,里面依旧是一堆调用,然后往下依次点击,每点击一个调用,就点击一个{},找到鼠标光点所在函数的返回值参数, # 10.对照查看作用域该参数里面params,encSeckey值, # 11.什么时候被加密,假如在第四行返回值参数我们能认识的值(类似key=value),但是在第三行就变成了不认识的值(就是第四步发现的样子,params=无规则字符串),那么就是在第三行被加密了 # 12.在该函数(见QQ截图)的第一行代码处设置断点,关掉之前的断点,点击那个暂停按钮把当前跑完, # 13.当显示为“||”时,重新刷新页面,点击播放键样子的按钮,让程序执行,找url末尾是get的,找到后,点击执行按钮旁边的下一步,并查看作用域的data # 14.发现加密过程 window.arsea(参数,XXX,XXX,XX),而params:encText,encSecKey:encSecKey # 15.data是i4m,也就是网易解密后的参数,请求方式:POST # 16.把window.arsea里面的参数一一拿出来,有些就放在console运行一下发现是定值 # 17.给d的函数里加断点,然后找到i的值,让他固定,因为它本来就是一个随机数 data = { "csrf_token": "", "cursor": "-1", "offset": "0", "orderType": "1", "pageNo": "1", "pageSize": "20", "rid": "R_SO_4_1927142874", "threadId": "R_SO_4_1927142874" } # 处理加密过程 """ function a(a=16) { 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);#取字符串的某一位 b return c # 16个随机字符串 } function b(a, b) { #a是要加密的 var c = CryptoJS.enc.Utf8.parse(b) #c 加密密钥,b就是密钥 , d = CryptoJS.enc.Utf8.parse("0102030405060708") , e = CryptoJS.enc.Utf8.parse(a) #e是数据 , 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) {d:数据data, var h = {} , i = a(16); #16个随机值 h.encText = b(d, g), #g是密钥 h.encText = b(h.encText, i), #这个地方就是params h.encSecKey = c(i, e, f), ##这个地方就是encSeckey i也是密钥,这个时候把i固定,e固定、f固定、那么encSecKey也是固定的 return h } """ e = '010001' f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7 ' g = '0CoJUm6Qyw8W8jud' i = "eMgnsUBjn5vbYOEQ" # h.encSecKey = c(i, e, f), ##这个地方就是encSeckey i也是密钥,这个时候把i固定,e固定、f固定、那么encSecKey也是固定的 def get_encSeckey(): return "948a56412ac45bceb6ca1b8148716597a55efb841d7457fed4059552a1123c18dd193a7ef4e9d04163f59d34e9c665a88dba31ed808c2c8a5cd9c27fe0eced56e1efd94d9a3ca3696c7bbef5081e0272368c465c40f1c0903f1cb99d70089ab85c30f7b1b9fd7b459559781b172ec3dafe75e03b3a3194858027f3126b0b7ed5 " # h.enctext经历了两次加密 # 还原d,默认收到的data是字符串 def get_params(data): first = enc_parms(data, g) second = enc_parms(first, i) return second # params def enc_parms(data, key): # 还原b加密过程 data = to_16(data) aes = AES.new(key=key.encode("utf-8"), IV="0102030405060708".encode("utf-8"), mode=AES.MODE_CBC) bs = aes.encrypt(data.encode("utf-8")) # 加密要求,加密内容的长度必须是16的倍数 return str(b64encode(bs), "utf-8") # 处理ValueError: Data must be padded to 16 byte boundary in CBC mode def to_16(data): pad = 16 - len(data) % 16 data += chr(pad) * pad return data resp = requests.post(url, data={ "params": get_params(json.dumps(data)), "encSecKey": get_encSeckey() }) print(resp.text) resp.close()