网页抓取解密(网易云技能点界面概况静态网页 )
优采云 发布时间: 2021-12-04 12:01网页抓取解密(网易云技能点界面概况静态网页
)
技能点界面概览静态网页
网易云仍然有一些网页,其网址一般会随着页面变化而变化。您只需要抓取网页进行分析。
动态网页
但是随着前后端分离的普及,数据分离的好处是显而易见的。越来越多的数据使用ajax渲染。即便如此,网易云的评论也是如此。
前后端分离刚刚火起来,当时很多网站并没有太多防备的借口。它让很多网站 很容易得到结果。到目前为止,有很多这样的借口。这种网站爬行就是傻瓜式爬行。
但是随着前端技术的发展,接口变得越来越难。拿网易云的评论来说:它的参数很混乱。
这串数字究竟是什么?很多人看到这样的数据会选择放弃。那我给你解开。
页面分析 step1:查找参数
可以看到,有两个参数,一个是params,一个是encSecKey,都是加密的。我们必须分析它的来源。F12 开源并搜索 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加密 偏移量为60708 两个key不同 函数c是RSA加密的三个参数 整体算法流程差不多有点了解了。
暂时停在这里,不分析函数,我们在分析数据。
step3:分析参数
返回 var bYc7V = window.asrsea(JSON.stringify(i3x), bkY2x(["流泪", "强"]), bkY2x(VM8E.md), bkY2x(["爱心", "girl", "horrified ", "Laughing"])) 这个函数。直觉上我能感觉到有些数据肯定和我们的核心参数没有关系,顶多跟时间戳有关。
找到bky2x的来源,
再去寻找,其实是没有必要的。寻找这种功能。可以复制到vscode中查找根本原因。分析,这里就不一一介绍了。只是中断分析!看看他是如何执行的。
其实抓多次就会发现最后三个参数是固定的(非交互数据)。
然而,我最想要的是第一个参数
你心里的参数是这样的,所以和预期的差不多,只有第一个参数和我们的参数有关。offset是page*20,R_SO_4_songid是当前歌曲的id。其实这时候你的i和encSecKey就可以一起保存了。因为上面分析说这个i是随机生成的,encSecKey和我们的核心参数无关,而是和i有关,所以我们需要记录一个组。用作 ESA 加密参数和 post 请求参数。
你现在是不是很兴奋,因为我真的很想浮出水面。
第4步:检查
这一步也是很重要的一个环节,因为你会在它的js里面找到。
网易会做些什么吗?下载原创js进行测试。找到了哈哈,结果是一致的。那么就不需要再次更改该加密算法的代码。
架构图是
step5:转换为python代码
AES的cbc模式的代码需要用Python克隆。达到加密的效果,测试一下。发现同样的结果很好
编写爬虫
让我们开始编写一个爬虫。首先使用邮递员测试这些参数。
没问题,写个爬虫。根据你喜欢的兄弟。输入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=9ca17ae2e6ffcda170e2e6eed2d650989c9cd1d*敏*感*词*bb6b88eb2c84e979f9aaff773afb6fb83d950bcb19ecce92af0fea7c3b92a88aca898e24f93bafba6f63a8ebe9caad9679192a8b4ed67ede89ab8f26df78eb889ea53adb9ba94b168b79bb9bbb567f78ba885f96a8c87a0aaf13ef7ec96a3d64196eca1d3b12187a9aedac17ea8949dccc545af918fa6d84de9e8b885bb6bbaec8db9ae638394e5bbea72f1adb7a2b365ae9da08ceb5bb59dbcadb77ca98bad8be637e2a3'
}
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": "c81160c64a08feb6cfed91c1619d5bffd05dd278b685c94a748689edf035ee0436b66aa7019927ce0fedd26aee9a22cdc6743e58a120f9db0126ebb2e61dae3f7ee21088eb747f829bceed9a5bbb9ee7a2eecf1a358fea*敏*感*词*31acaab17c95b8491a6a955f7c17a02a3e7886390c2cb3b981f4ccbd5163a566d27ace95db073401",
}
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)