Python爬虫之js加密破解,抓取网易云音乐评论生成词云

python

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  requests

import urllib.parse

import base64

from wordcloud import WordCloud

import jieba.analyse

import matplotlib.pyplot as plt

from bs4 import BeautifulSoup

from Crypto.Cipher import AES

header={"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 # 16

length = len(text)

bytes_length = len(bytes(text, encoding="utf-8"))

# tips:utf-8编码时,英文占1个byte,而中文占3个byte

padding_size = length if(bytes_length == length) else bytes_length

padding = bs - padding_size % bs

# tips:chr(padding)看与其它语言的约定,有的会使用""

padding_text = chr(padding) * padding

return text + padding_text

def 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 result

def 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_en

print(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=0

text=""

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

回到顶部