Python爬虫之js加密破解,抓取网易云音乐评论生成词云
js破解历程
- 前言
- 技能点
- 界面概况
- 静态网页动态网页
- 页面解析
- step1: 找参数step2:分析js函数step3:分析参数step4: 校验step5:转为python代码
- 编写爬虫
很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!??¤
QQ群:623406465
前言
网络爬虫的大障碍,就是各种加密。这其中包过登录的验证码以及加密。js混淆、js参数加密等等。其实以前也就了解过js加密。但是没有深入研究,借着这次实践研究了一下网易云音乐的加密方式。
博主通过网易云音乐评论加密的实例来做个学习过程的分析和分享。
如果有问题或者不懂的地方可以关注我的微信公众号(bigsai),联系我。
技能点
- 前端:js知识(比较重要)、谷歌浏览器debug、抓包、打断点调试能力(必须)。以及js各种加密函数(了解).
- python:基础的请求requests。Crypto.Cipher加密解密模块。
- 其他:postman(模拟请求使用),良好的思维能力和分析能力。(加密算法有些乱),还有一点就是js加密转python的代码实现。
界面概况
静态网页
对于一般的url随着页面的变化而变化的页面,网易云还是有的,你只需要抓取网页进行分析即可。
动态网页
但随着前后端分离的流行,以及数据分离好处明显。越来越多的数据采用ajax渲染。而网易云的评论即使如此。
在前后端分离刚火,那时很多网站对借口并没有太大的防护措施。就使得很多网站轻松获取结果。至今也有很多这样的借口存在,这种网站爬去就是傻瓜式爬取。
然而随着前段技术的发展,接口也变的越来越棘手。就拿网易云的评论来说:它的参数就让人很懵逼。
这一串串数字到底是啥。很多人见到这样的数据就会选择放弃。那么让我为你解开它什么的面纱。
页面解析
step1: 找参数
你可以看的到,它的参数有两个,一个是params,一个是encSecKey并且都是经过加密的,我们就要分析它的源头。F12打开source搜索encSckey.
"在查找这个js内部的encSecKey,发现原来在这里,经过断点调试发现这里就是最终参数的结果。
step2:分析js函数
这个js有4w多行,如何能在4w多行js中找到有用的信息,然后理清楚这里的思路呢?
这就需要你的抽象和逆向思维了。来,咱么开始分析。
var bYc7V = window.asrsea(JSON.stringify(i3x), bkY2x(["流泪", "强"]), bkY2x(VM8E.md), bkY2x(["爱心", "女孩", "惊恐", "大笑"]));e3x.data
= k4o.cz4D({params: bYc7V.encText,encSecKey: bYc7V.encSecKey
})
上面这段代码中就是来源,我们先不管这个JSON.stringify(i3x)这些参数是啥,先搞清楚window.asrsea是什么。在上面不远处你会发现:
这个就是d函数才是所有数据,方法的根源,d、e、f、g四个参数就是我们刚刚说的不要管的参数。
从这个函数就是分析:encText是经过两次b()函数,encSecKey是经过c()函数,执行的一个参数。注意其中i参数来源是a(16).网上看看这些函数。
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
}
可以发现a(16)就是一个随机生成的数,所以我们不需要管他。而b目前来看是AES的cbc模式加密。那么这个encText生成的规则我们就很清楚了。两次AES的cbc加密。其中偏移量为0102030405060708固定不变。两次的key不同。而函数c就是三个参数进行RSA加密。整个算法大体流程差不多稍微了解。
到这里先停一下,不要在分析函数了,我们在分析分析数据。
step3:分析参数
再回到var bYc7V = window.asrsea(JSON.stringify(i3x), bkY2x(["流泪", "强"]), bkY2x(VM8E.md), bkY2x(["爱心", "女孩", "惊恐", "大笑"]))这个函数。凭直觉能够感觉得到有些数据一定跟我们的核心参数无关,最多跟时间戳有关。
查找bky2x源头,
再找的话其实没必要,这类函数你找找。可以复制到vscode溯源找到根源。分析,在这里就不繁琐介绍。直接打断点分析吧!看看他是怎么执行的。
其实多次抓你会发现后三个参数是固定不变的(非交互型数据)。
然而最想要的是第一个参数
你心心年年的参数原来长这个样,那么和预想差不多,仅仅第一个参数和我们的参数有关。offset就是页面*20,R_SO_4_ songid就是当前这首歌的id.其实到这个时候,你的i和encSecKey可以一起保存了。因为上面分析说过,这个i是随机生成,而encSecKey也和我们核心参数无关,但是和i相关,所以要记录一组。用作ESA加密的参数和post请求的参数。
现在的你是不是很激动,因为真想即将浮出水面。
step4: 校验
这步骤也是很重要的一环,因为你在它的js中会发现。
网易是否会动手脚呢?下载原始的js进行测试。发现哈哈,结果一致。那么就不需要更改再仔细查看那段加密算法的代码了。
架构图为
step5:转为python代码
需要将AES的cbc模式的代码用Python克隆。达到加密的效果,测试一下。发现结果一致nice
编写爬虫
下面就开始编写爬虫。先用postman测试需要那些参数。
没问题,编写爬虫。根据你喜欢的哥。把id输进去,生成你爱的词云!一首光辉岁月送给大家!
import requestsimport urllib.parse
import base64
from wordcloud import WordCloudimport jieba.analyse
import matplotlib.pyplot
as pltfrom bs4 import BeautifulSoupfrom Crypto.Cipher import AESheader
={"User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36",#
"Postman-Token":"4cbfd1e6-63bf-4136-a041-e2678695b419","origin":"https://music.163.com",#
"referer":"https://music.163.com/song?id=1372035522",#
"accept-encoding":"gzip,deflate,br","Accept":"*/*","Host":"music.163.com","content-lenth":"472","Cache-Control":"no-cache","content-type": "application/x-www-form-urlencoded","Connection":"keep-alive",#
"Cookie":"iuqxldmzr_=32; _ntes_nnid=a6f29f40998c88c693bc910331bd6bea,1558011234325; _ntes_nuid=a6f29f40998c88c693bc910331bd6bea; _ga=GA1.2.2120707788.1559308501; WM_TID=pV2C%2BjTrRwBBAAERUVJojniTwk8%2B8Zta; JSESSIONID-WYYY=nvf%2BggodQRfcT%2BTvBRmANqMrsDeQCxRvqwFsxDr3eJvNNWhGYFhfCXKFkfAfOdbHhpCsMzT39mAeJ7ZamBQZbiwwtnSZD%5CPWRqKxD9t6dGKD3bTVjomjgB39DB07RNIWI32bYKa2H4fg1qQgqI%2FR%2B%2Br%2BZXJvgFg1Vh%2FA2XRj9S4p0EMu%3A1560927288799; WM_NI=DthwcEQf5Ew2NbTIZmSNhSnm%2F8VWsg5RxhkYogvs2luEwZ6m5UhdzbHYPIr654ZBWKV4o22%2BEwb9BvdLS%2BFOmOAEUG%2B8xd8az4CX%2FiAL%2BZkz3syA0onCPkhQwCtL4pkUcjg%3D; WM_NIKE=9ca17ae2e6ffcda170e2e6eed2d650989c9cd1dc4bb6b88eb2c84e979f9aaff773afb6fb83d950bcb19ecce92af0fea7c3b92a88aca898e24f93bafba6f63a8ebe9caad9679192a8b4ed67ede89ab8f26df78eb889ea53adb9ba94b168b79bb9bbb567f78ba885f96a8c87a0aaf13ef7ec96a3d64196eca1d3b12187a9aedac17ea8949dccc545af918fa6d84de9e8b885bb6bbaec8db9ae638394e5bbea72f1adb7a2b365ae9da08ceb5bb59dbcadb77ca98bad8be637e2a3"}
def pkcs7padding(text):
""" 明文使用PKCS7填充最终调用AES加密方法时,传入的是一个byte数组,要求是16的整数倍,因此需要对明文进行处理
:param text: 待加密内容(明文)
:
return:""" bs = AES.block_size # 16length
= len(text)bytes_length
= len(bytes(text, encoding="utf-8"))# tips:utf
-8编码时,英文占1个byte,而中文占3个bytepadding_size
= length if(bytes_length == length) else bytes_lengthpadding
= bs - padding_size % bs# tips:chr(padding)看与其它语言的约定,有的会使用
""padding_text
= chr(padding) * paddingreturn text + padding_textdef encrypt(key, content):
""" AES加密key,iv使用同一个
模式cbc
填充pkcs7
:param key: 密钥
:param content: 加密内容
:
return:""" key_bytes = bytes(key, encoding="utf-8")iv
= bytes("0102030405060708", encoding="utf-8")cipher
= AES.new(key_bytes, AES.MODE_CBC, iv)# 处理明文
content_padding
= pkcs7padding(content)# 加密
encrypt_bytes
= cipher.encrypt(bytes(content_padding, encoding="utf-8"))# 重新编码
result
= str(base64.b64encode(encrypt_bytes), encoding="utf-8")return resultdef getcomment(songid,page):
url
="https://music.163.com/weapi/v1/resource/comments/R_SO_4_"+songid+"?csrf_token="print(url)
formdata
= {"params": "","encSecKey": "c81160c64a08feb6cfed91c1619d5bffd05dd278b685c94a748689edf035ee0436b66aa7019927ce0fedd26aee9a22cdc6743e58a120f9db0126ebb2e61dae3f7ee21088eb747f829bceed9a5bbb9ee7a2eecf1a358feac431acaab17c95b8491a6a955f7c17a02a3e7886390c2cb3b981f4ccbd5163a566d27ace95db073401",}
aes_key
= "0CoJUm6Qyw8W8jud"## 不变的print(
"aes_key:" + aes_key)# 对英文加密
source_en
= "{"rid":"R_SO_4_"+songid+"","offset":""+str(page*20)+"","total":"false","limit":"20","csrf_token":""}"#offset自己该
print(source_en)
encrypt_en
= encrypt(aes_key, source_en)#第一次加密print(encrypt_en)
aes_key
="3Unu7SzdXGctW1vA"encrypt_en
= encrypt(aes_key, str(encrypt_en)) # 第二次加密print(encrypt_en)
formdata[
"params"]=encrypt_enprint(formdata[
"params"])formdata
= urllib.parse.urlencode(formdata).encode("utf-8")print(formdata)
req
= requests.post(url=url, data=formdata, headers=header)return req.json()if __name__ == "__main__":songid
="346576"page
=0text
=""for page in range(10):
comment=getcomment(songid,page)
comment=comment["comments"]
for va in comment:
print (va["content"])
text+=va["content"]
ags = jieba.analyse.extract_tags(text, topK=50) # jieba分词关键词提取,40个
print(ags)
text = "".join(ags)
backgroud_Image = plt.imread("tt.jpg") # 如果需要个性化词云
wc = WordCloud(background_color="white",
width=1200, height=900,
mask=backgroud_Image, # 设置背景图片
#min_font_size=50,
font_path="simhei.ttf",
max_font_size=200, # 设置字体最大值
random_state=50, # 设置有多少种随机生成状态,即有多少种配色方案
) # 字体这里有个坑,一定要设这个参数。否则会显示一堆小方框wc.font_path="simhei.ttf" # 黑体
# wc.font_path="simhei.ttf"
my_wordcloud = wc.generate(text)
plt.imshow(my_wordcloud)
plt.axis("off")
plt.show() # 如果展示的话需要一个个点
file = "image/" + str("aita") + ".png"
wc.to_file(file)
以上是 Python爬虫之js加密破解,抓取网易云音乐评论生成词云 的全部内容, 来源链接: utcz.com/z/530757.html