700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 解密网易云音乐评论js加密参数 实现分词处理制作词云图

解密网易云音乐评论js加密参数 实现分词处理制作词云图

时间:2018-12-10 18:32:46

相关推荐

解密网易云音乐评论js加密参数 实现分词处理制作词云图

前言:

很多人都说网易云音乐的一条评论是一个故事, 细细一看的话, 你会发现有些评论确实很真, 很性情。

抓取网易云音乐评论主要涉及了参数的加密问题,这篇博客主要是剖析一下如何进行参数的加密以及解密剖析

这里使用的是java进行加密实现数据的抓取。

爬虫有三难:

登录难 如: 淘宝, 12306参数加密 如: 网易云音乐评论, 企查查, 天眼查,响应(内容)加密 如: 企查查, 天眼查

确立需求

获取“电灯胆”歌词评论, 然后对歌词进行分词处理,获取高频词,制作词云图等

这里主要涉及的的技术点有:

F12的抓包以及快速定位链接网页debug js在爬虫中的使用爬虫参数加密的剖析过程java分词工具word的使用mysql 统计相同

先来看一下整体效果

图一 图二使用的是 图悦 进行分词统计并且生成词云图

/picdata/index.php#

图三使用的是java word 分词库进行分词, 结合数据库进行统计词频等生成的词云图

具体步骤如下:

步骤01: 我们随机选择一首歌曲进入页面, 拖到底部查看评论页面

然后点击下一页, 网页会请求后台获取数据, 我们通过F12抓包可以知道链接地址以及参数等, 如下图:

步骤02: 参看传递的参数, 如下图

我们可以了解到

url:/weapi/v1/resource/comments/R_SO_4_1379057027?csrf_token=

method: post

content-type: application/x-www-form-urlencoded

params:必要参数

encSecKey: 必要参数

步骤03: 解析参数来源

从图一中可以知道发起请求的js是core_97f1bfe….js?97f1bfe

那么进入这个js查看其源码, 可以发现这个core_xxxx.js的东西竟然是一个4万5千多行的文件

不过不要慌, 如果真的去看完这个多代码都可以前端 js了, 我想这篇博客也是不知道猴年马月才可以写了

步骤04: 快速定位加密的js位置

ctrl + f 搜索关键字 “encSecKey”

可以知道了生成参数params encSecKey的参数位置了

接着往上看两行代码

var bUP7I = window.asrsea(JSON.stringify(i8a), brF9w(["流泪", "强"]), brF9w(WZ4d.md), brF9w(["爱心", "女孩", "惊恐", "大笑"]));

发现是一个 window.asrsea的方法产生的一个对象bUP7I

在这里留一个心眼, brF9w 这是一个方法, 传递了一些乱七八糟的参数, 但是我在断点了好多次都发现是这几个奇奇怪怪的参数, 非常有意思的网易云音乐呀…

因此我们在这里可以理解为调用的window.asrsea()方法,

第一个参数是一个字符串,=>因为看到了JSON.stringify()这个方法

第二个参数是一个常量

第三个参数是一个常量

第四个参数是一个常量

步骤05: 找到五个核心方法(a b c d e)

接着我们ctrl + f 搜索关键字 “asrsea”

如下图, 我们这个五个方法进行了深入的了解以及剖析, 就是这几个方法花了我一个星期的空闲时间, 主要是不理解机密, 捣腾了

window.asrsea = d,

window.ecnonasr = e

可以了解到d方法赋值给window.asrsea, e赋值给window.ecnonasr

因此图四调用的window.asrsea, 实际上就是d方法

步骤06: debug核心方法d

在12896行打一个断点, 我们查看一下四个参数究竟是什么东东

参数d:

{“rid”:“R_SO_4_1379057027”,“offset”:“100”,“total”:“false”,“limit”:“20”,“csrf_token”:""}

参数e:

010001

参数f:

00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7

参数g:

0CoJUm6Qyw8W8jud

关键的源码

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}function e(a, b, d, e) {var f = {};return f.encText = c(a + e, b, d),f}window.asrsea = d,window.ecnonasr = e

结合步骤05的图解, 可以知道方法b是一个加密方法,方法d是一个参数组装方法,

细看方法d, 会发现调用了两次方法b, 也就是加密两次了, 第一次是对参数d, g加密, g是一个常量, 上面以及解析了, 第二次是对第一次机密的结果再次进行加密, 参数i是一个调用了a方法的16为随机数, 因此i也是可以理解为一个常量值, 通过debug, 就可以查到这个常量是什么了

接着再看 h.encSecKey = c(i, e, f),这行代码

由于 i 可以理解为常量, e 传入d方法的时候也是常量, f 传入d方法的时候也是常量,

因此可以理解为h.encSecKey也是一个常量, 我们只需要debug, 获取到一个h.encSecKey即可( 但是这个encSecKey和参数 i 需要 同一次debug出来的才可以, 否则有问题的…)

步骤07: java实现前端的加密算法(js方法b)

由于这里找了很多资料, 直接提供这个方法出来

/bicheng4769/article/details/80811676

/question/1819427615658816228.html

import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import org.springframework.util.Base64Utils;/*** @description AESEncrypt加密工具* @date -08-24 23:51:19* @author houyu for.houyu@*/public class AESEncrypt {public static String encrypt(String content, String sKey) {/*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()}// 说明: 前段js没有使用填充模式, 默认使用了PKCS7Padding//// /question/1819427615658816228.html// CryptoJS.enc.Utf8.parse方法才可以将key转为128bit的。好吧,既然说了是多次尝试,那么就不知道原因了,后期再对其进行更深入的研究。// 字符串类型的key用之前需要用uft8先parse一下才能用//// 后端使用的是PKCS5Padding,但是在使用CryptoJS的时候发现根本没有这个偏移,查询后发现PKCS5Padding和PKCS7Padding是一样的东东,使用时默认就是按照PKCS7Padding进行偏移的//// CryptoJS.AES >> 算法// CBC >> 模式// 0102030405060708>> 偏移量//// 由于CryptoJS生成的密文是一个对象,如果直接将其转为字符串是一个Base64编码过的,在encryptedData.ciphertext上的属性转为字符串才是后端需要的格式。*/try {byte[] encryptedBytes;byte[] byteContent = content.getBytes("UTF-8");// 获取cipher对象,getInstance("算法/工作模式/填充模式")Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");// 采用AES方式将密码转化成密钥SecretKeySpec secretKeySpec = new SecretKeySpec(sKey.getBytes(), "AES");// 初始化偏移量IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes("UTF-8"));//cipher对象初始化 init(“加密/解密,密钥,偏移量”)cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);//按照上面定义的方式对数据进行处理。encryptedBytes = cipher.doFinal(byteContent);return new String(Base64Utils.encode(encryptedBytes), "UTF-8");} catch (Exception e) {e.printStackTrace();}return null;}}

步骤08: 验证js加密之后的内容与java加密之后的内容是否一致(方法b)

首先控制相同变量

参数d:

{“rid”:“R_SO_4_1379057027”,“offset”:“100”,“total”:“false”,“limit”:“20”,“csrf_token”:""}

参数g:

0CoJUm6Qyw8W8jud

浏览器控制台输入

b(’{“rid”:“R_SO_4_1379057027”,“offset”:“100”,“total”:“false”,“limit”:“20”,“csrf_token”:""}’, ‘0CoJUm6Qyw8W8jud’)

结果会报如下图错误

因此我们需要先定义方法b, 直接把方法b的源码丢到控制台即可

后端java加密结果

至此 以及实现了java后端加密和js加密一致的内容, 接下来就是比较简单的了,看一下封装对应前段的几个方法即可

import java.util.HashMap;import java.util.Map;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import org.springframework.util.Base64Utils;/*** @description AESEncrypt加密工具* @date -08-24 23:51:19* @author houyu for.houyu@*/public class AESEncrypt {/*** @description 对应网易云音乐的方法b* @date -08-25 00:05:05* @author houyu for.houyu@*/public static String encrypt(String content, String sKey) {/*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()}// 说明: 前段js没有使用填充模式, 默认使用了PKCS7Padding//// /question/1819427615658816228.html// CryptoJS.enc.Utf8.parse方法才可以将key转为128bit的。好吧,既然说了是多次尝试,那么就不知道原因了,后期再对其进行更深入的研究。// 字符串类型的key用之前需要用uft8先parse一下才能用//// 后端使用的是PKCS5Padding,但是在使用CryptoJS的时候发现根本没有这个偏移,查询后发现PKCS5Padding和PKCS7Padding是一样的东东,使用时默认就是按照PKCS7Padding进行偏移的//// CryptoJS.AES >> 算法// CBC >> 模式// 0102030405060708>> 偏移量//// 由于CryptoJS生成的密文是一个对象,如果直接将其转为字符串是一个Base64编码过的,在encryptedData.ciphertext上的属性转为字符串才是后端需要的格式。*/try {byte[] encryptedBytes;byte[] byteContent = content.getBytes("UTF-8");// 获取cipher对象,getInstance("算法/工作模式/填充模式")Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");// 采用AES方式将密码转化成密钥SecretKeySpec secretKeySpec = new SecretKeySpec(sKey.getBytes(), "AES");// 初始化偏移量IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes("UTF-8"));//cipher对象初始化 init(“加密/解密,密钥,偏移量”)cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);//按照上面定义的方式对数据进行处理。encryptedBytes = cipher.doFinal(byteContent);return new String(Base64Utils.encode(encryptedBytes), "UTF-8");} catch (Exception e) {e.printStackTrace();}return null;}/** 以下参数是js debug获取到的 */public static final String i = "C3Ba8sQUIHbPHC1Z";public static final String e = "";public static final String f = "";public static final String g = "0CoJUm6Qyw8W8jud";public static final String encSecKey = "7c23b7a80684cee814ecdc6252cc66e53a4df5890b1299783e5c575c709e7c8c22d98edc4074fc31b9bf7458e1ab6452c42fde55fcbd9b765f049da3809703686fc86b43ff757a2fa9eb77c0b04a51f02efb3e0ade116454561a6f2aefe89f6d611343383eaf643dce13b4ad1709ea2f8215f922c1a014d7fd79adcd0fa107a4";/*** @description 对应网易云音乐方法d, 其中参数d f g在外边定义好* @date -08-25 00:06:51* @author houyu for.houyu@*/public static Map<String, Object> methodD(String pageObject) {String encText = encrypt(pageObject, g);encText = encrypt(encText, i);// C3Ba8sQUIHbPHC1Z// 这里就不必要调用方法c了,因为这里是一个固定值, 直接前段js debug出来即可Map<String, Object> paramMap = new HashMap<>(2);paramMap.put("params", encText);paramMap.put("encSecKey", encSecKey);return paramMap;}public static Map<String, Object> getParamMap(String rid, int pageIndex, int pageSize){String s = "{\"rid\":\"%s\",\"offset\":\"%s\",\"total\":\"false\",\"limit\":\"%s\",\"csrf_token\":\"\"}";s = String.format(s, rid, (pageIndex - 1) * pageSize, pageSize);return methodD(s);}}

具体源码在上面的git

/HouYuSource/spider/blob/master/src/main/java/cn/shaines/spider/module/music/Music163Spider.java

代理池抓取多线程抓取

待完善的地方是:重试机制的建立, 也就是说某一页失败了,加入重新抓取队列中。

最后的话

单个IP抓取一定数量之后就会被封IP,建议加上IP池(下面是封IP之后, 网页的评论列表都打不开了)

网易后端限制了中间页数的获取, 也就是说大概有2/5获取不到的.

我的CSDN:/JinglongSource

我的博客:

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。