网上也有很多人写过的,但感觉可能都不是很细,特此写了一份,不足之处请大家多多包涵 就以这首<凉凉>为例,给大家分享一下网易云歌词的获取
歌曲地址:https://music.163.com/#/song?id=536622115
F12找到歌词的请求地址
通过观察可以发现就是这params和encSecKey不知道怎么来的 别急!肯定是js算出来的,慢慢从找 将encSecKey参数放到请求的js文件里面检索就能发现下面这个,大致锁定就在这了
好了,去source里面找到这个js我们打个断点 我这里的请求url变量是Y0x,要观察这个值,目前我们是去抓取歌词,所以其他的请求我们都跳过,直到 Y0x=“/api/song/lyric”,如下图所示
当执行到下图所示时,
现在来分析下 主要关注的
var bRB0x = window.asrsea(JSON.stringify(i9b), brX3x(["流泪", "强"]), brX3x(Ua6U.md), brX3x(["爱心", "女孩", "惊恐", "大笑"]));
i9b我们已知了
i9b={ id : "536622115",//需要关注的就是你了,这个值就是歌曲的id值 lv : -1, tv :-1, csrf : "" }
brX3x(["流泪", "强"] brX3x(Ua6U.md) brX3x(["爱心", "女孩", "惊恐", "大笑"]) 这三个值,都是固定的,这个暂时不需要过多的关注,先把他们的值给记一下
brX3x(["流泪", "强"]="010001" brX3x(Ua6U.md)="00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7" brX3x(["爱心", "女孩", "惊恐", "大笑"])="0CoJUm6Qyw8W8jud"
继续去看看window.asrsea(),这个方法里面做了啥子
执行的是这个function d
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 }
这四个值我们也已经得知了, i=a(16),仔细看a(16)好像就是个随机数,先不管 h.encText = b(d, g) d,g我们都知道了,d就是有歌曲id的json,g是brX3x(["爱心", "女孩", "惊恐", "大笑"])是个固定值
d="{"id" : "536622115","lv ": -1,"tv" :-1,"csrf" : ""} g="0CoJUm6Qyw8W8jud"
接下来看看h.encText = b(d, g)这个,AES加密,iv值是固定写死的,这个好办用java去写个AES加密
package com.dcs.common.utils; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * AES 是一种可逆加密算法,对用户的敏感信息加密处理 * 对原始数据进行AES加密后,在进行Base64编码转化; * @author Antoneo * @create 2018-06-28 22:16 **/ public class AesCBCUtils { /** * 加密用的Key 可以用26个字母和数字组成 * 此处使用AES-128-CBC加密模式,key需要为16位。 */ /** * 加密 * @param sSrc 明文 * @param encodingFormat 字符集 * @param sKey 秘钥 * @param ivParameter 向量iv * @return * @throws Exception */ public static String encrypt(String sSrc, String encodingFormat, String sKey, String ivParameter) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); byte[] raw = sKey.getBytes(); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());//使用CBC模式,需要一个向量iv,可增加加密算法的强度 cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte[] encrypted = cipher.doFinal(sSrc.getBytes(encodingFormat)); return new BASE64Encoder().encode(encrypted);//此处使用BASE64做转码。 } /** * 解密 * @param sSrc 明文 * @param encodingFormat 字符集 * @param sKey 秘钥 * @param ivParameter iv向量 * @return * @throws Exception */ public static String decrypt(String sSrc, String encodingFormat, String sKey, String ivParameter) throws Exception { try { byte[] raw = sKey.getBytes("ASCII"); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes()); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);//先用base64解密 byte[] original = cipher.doFinal(encrypted1); String originalString = new String(original,encodingFormat); return originalString; } catch (Exception ex) { return null; } } public static void main(String[] args) throws Exception { String a=encrypt("{"id":"536622115","lv":-1,"tv":-1,"csrf_token":""}","utf-8","0CoJUm6Qyw8W8jud","0102030405060708"); System.out.println(a); } }
运行下看看
没问题,java和js执行结果一致 接着往下看 h.encText = b(h.encText, i),又来一遍,i的值a(16)就把他当成一个固定值
a(16)="Sx7KnWt4ttr85X0b"
往下走
h.encSecKey = c(i, e, f) function c(a, b, c) { var d, e; return setMaxDigits(131), d = new RSAKeyPair(b,"",c), e = encryptedString(d, a) }
在这个c方法里面, a就是传过来i也就是a(16), b就是brX3x(["流泪", "强"], c就是brX3x(Ua6U.md),
a="Sx7KnWt4ttr85X0b" b="010001" c="00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
慢点...仔细想想好像有点猫腻呢 a是随机值,可以理解成固定值,b和c都是固定值 .... 这就欢快了,理一下思路 这就是说只要这个a(16)这个随机值不变,那个c的结果是固定的,就是encSecKey是固定的(js执行下查看结果就ok了); 已知a(16),写好的AES加密已经可以把encText的值给算出来了 好像已经结束了 。。。。(≧∇≦)ノ
encText="+o2ZcetTMTEWY1KLilzaGDKZwZ4dAgEZ+xke0FxnHcGs9TlVM91xyVhOZWc5Xe1aoHUQdMESSdOv yeYQZ1K02Q==" encSecKey="252e2abdc25a5c8e4e4131b88db3df7d01ab4a139249b78e653b97ab52f53b873993b86648e54daa3a99eeb20fd3b2c4d1d551231a152bfa56ed0a13baae9243f978bf1fbcde4e70b25087fd0aeef413a698a0a37567a550876f8cdeedb25cf359f54532eb2681f63641a4fa98b837fb9978c3296b2923bca8f5d1661d3ec5dc"
去验证一下吧 代码都放出来: AesCBCUtils.java
package com.dcs.common.utils; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * AES 是一种可逆加密算法,对用户的敏感信息加密处理 * 对原始数据进行AES加密后,在进行Base64编码转化; * @author Antoneo * @create 2018-06-28 22:16 **/ public class AesCBCUtils { /** * 加密用的Key 可以用26个字母和数字组成 * 此处使用AES-128-CBC加密模式,key需要为16位。 */ /** * 加密 * @param sSrc 明文 * @param encodingFormat 字符集 * @param sKey 秘钥 * @param ivParameter 向量iv * @return * @throws Exception */ public static String encrypt(String sSrc, String encodingFormat, String sKey, String ivParameter) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); byte[] raw = sKey.getBytes(); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());//使用CBC模式,需要一个向量iv,可增加加密算法的强度 cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte[] encrypted = cipher.doFinal(sSrc.getBytes(encodingFormat)); return new BASE64Encoder().encode(encrypted);//此处使用BASE64做转码。 } /** * 解密 * @param sSrc 明文 * @param encodingFormat 字符集 * @param sKey 秘钥 * @param ivParameter iv向量 * @return * @throws Exception */ public static String decrypt(String sSrc, String encodingFormat, String sKey, String ivParameter) throws Exception { try { byte[] raw = sKey.getBytes("ASCII"); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes()); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);//先用base64解密 byte[] original = cipher.doFinal(encrypted1); String originalString = new String(original,encodingFormat); return originalString; } catch (Exception ex) { return null; } } public static void main(String[] args) throws Exception { String a=encrypt("{/"id/":/"536622115/",/"lv/":-1,/"tv/":-1,/"csrf_token/":/"/"}","utf-8","0CoJUm6Qyw8W8jud","0102030405060708"); System.out.println(a); } }
WyyParams.java
package com.dcs.spider.wyy; import lombok.Data; /** * 参数 * * @author Antoneo * @create 2018-06-28 22:46 **/ @Data public class WyyParams { private String encText; private String encSecKey; }
WyySpider.java
package com.dcs.spider.wyy; import com.dcs.common.utils.AesCBCUtils; import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.util.HashMap; import java.util.Map; /** * 网易云抓取 * * @author Antoneo * @create 2018-06-28 22:21 **/ public class WyySpider { private static String iv="0102030405060708"; private static String g="0CoJUm6Qyw8W8jud"; private static String i="Sx7KnWt4ttr85X0b"; private static String encSecKey="252e2abdc25a5c8e4e4131b88db3df7d01ab4a139249b78e653b97ab52f53b873993b86648e54daa3a99eeb20fd3b2c4d1d551231a152bfa56ed0a13baae9243f978bf1fbcde4e70b25087fd0aeef413a698a0a37567a550876f8cdeedb25cf359f54532eb2681f63641a4fa98b837fb9978c3296b2923bca8f5d1661d3ec5dc"; private static String a(int a){ int d,e; String b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String c=""; for(d=0;a>d;d+=1){ e=(int)Math.floor(Math.random()*b.length()); c+=b.charAt(e); } return c; } private static String b(String d,String g) throws Exception { return AesCBCUtils.encrypt(d,"utf-8",g,iv).replace("/r/n",""); } /** * 获得歌词url的参数对象 */ public static WyyParams getParams(String id) throws Exception { String d=String.format("{/"id/":/"%s/",/"lv/":-1,/"tv/":-1,/"csrf_token/":/"/"}",id); String encText=b(d,g); encText=b(encText,i); WyyParams params=new WyyParams(); params.setEncSecKey(encSecKey); params.setEncText(encText); return params; } public static Document spider(String id){ Document doc= null; Map data=new HashMap(); Map head=new HashMap(); try { WyyParams params=WyySpider.getParams(id); Connection con=Jsoup.connect("https://music.163.com/weapi/song/lyric?csrf_token="); data.put("encSecKey",params.getEncSecKey()); data.put("params",params.getEncText()); head.put("Content-Type","application/x-www-form-urlencoded"); head.put("Accept","*/*"); con.headers(head); con.data(data); doc= con.post(); } catch (Exception e) { e.printStackTrace(); } return doc; } public static void main(String[] args){ String id ="536622115"; Document doc=WyySpider.spider(id); if(null!=doc) { System.out.println(doc.text()); } } }
执行结果:
{"sgc":true,"sfy":false,"qfy":false,"lrc":{"version":4,"lyric":"[00:00.00] 作曲 : 谭旋/n[00:01.00] 作词 : 刘畅/n[00:03.19]编曲Arranger:尼克Ke Ni/n[00:04.41]制作人Producer:张梓轩Zixuan Zhang/倪睿思 Ruisi Ni/n[00:05.76]原唱Original Singer:张碧晨Bichen Zhang /杨宗纬Zongwei Yang/n[00:07.01]音乐总监Music director:梁翘柏Kubert Leung/n[00:08.32]混音Mixing Engineer:林梦洋Mengyang Lin/n[00:09.59]键盘Keyboard players:达日丹Ridan Da/杨阳Yang Yang/白宇轩Yuxuan Bai/n[00:10.96]贝斯Bassist:韩阳Yang Han/n[00:12.30]吉他Guitarist:Tommy /黄仲贤Zhongxian Huang/n[00:13.70]鼓Drummer:郝稷伦Jilun Hao/n[00:15.03]小号Trumpet Player:Ray/n[00:16.36]打击乐Percussionist:刘效松Xiaosong Liu/n[00:17.73]萨克斯/单簧管Saxes Player/Clarinet Player:Miles/n[00:19.01]萨克斯/长笛Saxes Player/Flute Player:Charlie/n[00:20.43]长号Trombone Player:谢燕辉Yanhui Xie/n[00:21.77]弦乐String Orchestra:靳海音®弦乐团 Jin HaiYin®String orchestra/n[00:23.18]和声Harmonic Orchestra:爱之音合音组Sound of love Chorus group/n[00:24.49]/n[00:37.04]女:/n[00:42.91]入夜渐微凉/n[00:44.90]繁花落地成霜/n[00:47.54]你在远方眺望/n[00:50.16]耗尽所有暮光/n[00:52.88]不思量自难相忘/n[01:02.96]/n[01:03.68]男:/n[01:04.22]夭夭桃花凉/n[01:06.16]前世你怎舍下/n[01:08.80]这一海心茫茫/n[01:11.51]还故作不痛不痒不牵强/n[01:18.11]都是假象/n[01:24.17]/n[01:24.65]女:/n[01:25.22]凉凉夜色为你思念成河/n[01:30.66]化作春泥呵护着我/n[01:35.20]/n[01:35.67]男:/n[01:35.85]浅浅岁月拂满爱人袖/n[01:40.45]片片芳菲入水流/n[01:46.44]/n[01:46.82]女:/n[01:47.11]凉凉天意潋滟一身花色/n[01:51.67]落入凡尘伤情着我/n[01:56.74]/n[01:57.02]男:/n[01:57.36]生劫易渡情劫难了/n[02:00.29]折旧的心还有几分前生的恨/n[02:08.04]还有几分/n[02:10.98]/n[02:11.39]女:/n[02:11.89]前生的恨/n[02:20.94]/n[02:35.51]女:/n[02:37.11]也曾鬓微霜/n[02:39.46]也曾因你回光/n[02:41.96]悠悠岁月漫长/n[02:44.69]怎能浪费时光/n[02:47.36]去流浪/n[02:51.73]去换成长/n[02:56.40]/n[02:57.69]男:/n[02:58.67]灼灼桃花凉/n[03:00.75]今生愈渐滚烫/n[03:03.32]一朵已放心上/n[03:06.05]足够三生三世背影成双/n[03:10.63]/n[03:10.82]女:/n[03:11.09]背影成双/n[03:13.05]/n[03:13.46]男:/n[03:13.73]在水一方/n[03:17.91]/n[03:18.41]女:/n[03:19.91]凉凉夜色为你思念成河/n[03:25.21]化作春泥呵护着我/n[03:30.11]/n[03:30.42]男:/n[03:30.74]浅浅岁月拂满爱人袖/n[03:35.17]片片芳菲入水流/n[03:41.06]/n[03:41.36]女:/n[03:41.63]凉凉天意潋滟一身花色/n[03:46.47]落入凡尘伤情着我/n[03:51.41]/n[03:51.75]男:/n[03:52.07]生劫易渡情劫难了/n[03:54.85]折旧的心还有/n[03:57.26]/n[03:57.55]合:/n[03:57.76]几分前生的恨/n[04:03.49]/n[04:12.95]女:/n[04:13.60]凉凉三生三世恍然如梦/n[04:18.45]须臾的年风干泪痕/n[04:23.38]/n[04:23.71]男:/n[04:24.05]若是回忆不能再相认/n[04:28.31]/n[04:28.65]合:/n[04:28.82]就让情分落九尘/n[04:34.26]/n[04:34.50]女:/n[04:34.83]凉凉十里何时还会春盛/n[04:39.73]又见树下一盏风存/n[04:44.65]/n[04:45.04]男:/n[04:45.34]落花有意流水无情/n[04:48.25]别让恩怨爱恨/n[04:50.62]/n[04:50.81]合:/n[04:50.98]凉透那花的纯/n[04:57.83]/n[04:58.29]女:/n[04:58.87]吾生愿牵尘/n[05:11.19]/n"},"tlyric":{"version":0,"lyric":null},"code":200} Process finished with exit code 0
换首歌再试一遍: https://music.163.com/#/song?id=863492743 执行结果:
{"sgc":true,"sfy":true,"qfy":true,"lrc":{"version":4,"lyric":"[00:00.00] 作曲 : 陈壹千/n[00:01.00] 作词 : 陈壹千/n[00:10.629]混音:陈壹千/n[00:17.695]终于还是没能等到结果/n[00:22.696]谎言将我淹没/n[00:25.505]伪装的人不是我/n[00:30.111]还傻傻的难过 以为你会懂/n[00:35.341]我才让你解剖/n[00:37.709]所有一切都因为你变得刺痛/n[00:45.398]终于还是下定决心走得干净利落/n[00:50.702]倒不如离开有你的地方/n[00:54.573]去别的城市找回我/n[00:58.213]都怪我太念旧/n[01:01.631]而你太过刻薄/n[01:03.717]从那以后/n[01:06.133]我学会了让眼泪偷偷的 往回流/n[01:17.541]还是回到了我们开始的地方/n[01:23.646]我们都只看到期待 却都忘了要等待/n[01:31.589]那种孩子理念在现实里面/n[01:35.645]最多撑不过一年/n[01:38.639]在有限时间打破所有的誓约/n[01:45.509]我现在才懂得一个人可以很快乐/n[01:52.653]虽然还会辗转反侧 想起时眼眶会热/n[01:59.463]而你早已忘了关于我的 好的坏的/n[02:07.189]在我离开以后/n[02:15.893]这次真的可以还你自由/n[02:19.977]我想分开会好过互相折磨/n[02:27.039]我还是习惯开着灯 熬到凌晨/n[02:32.366]就算睡着也不害怕/n[02:35.873]不如当作有个人在黑夜里陪我/n[02:42.066]我终于下定决心删除所有你的段落/n[02:48.480]也留一点尊严给自己以后见到你时不回头/n[02:55.623]我真的不在乎/n[02:58.975]但有时候却又控制不住/n[03:03.906]一点遗憾 也有一点不甘/n[03:11.610]还是回到了我们开始的地方/n[03:18.185]我们都只看到期待 却都忘了要等待/n[03:24.833]那种孩子理念在现实里面/n[03:29.401]最多撑不过一年/n[03:32.744]在有限时间打破所有的誓约/n[03:39.559]我现在才懂得一个人可以很快乐/n[03:46.593]虽然还会辗转反侧 想起时眼眶会热/n[03:53.240]而你早已忘了关于我的 好的坏的甜的苦的/n[04:01.426]在我离开你的城市以后/n[04:08.199]我不会再哭着求你为我最后一次擦眼泪/n[04:22.159]也在尽力忘了关于你的/n[04:26.264]只是想起时心有点疼/n"},"tlyric":{"version":0,"lyric":null},"code":200} Process finished with exit code 0
网易云的歌词获取到此结束了,教大家方法后可以用来获取网易云评论或者其他的什么 第一次写文章有点小激动... 谢谢支持!
简书地址: https://www.jianshu.com/p/c1595cbb11df