关于爬网易云音乐时使用fiddler调试JS遇到的问题?

1.

这几天一直想做个网站,关于网易云的,就是爬取网易云评论来进行展示。有了想法,就立马行动起来。我的准备是用python来爬取网易云评论。然后自己写个前端,进行展示。不需要太复杂。就是一个简单的页面就行了。通过js向我的后端发请求来获取评论。后端用flask来写一个api。随机返回十几个评论。想着蛮简单的,但是,做起来就遇到了不少困难。

困难1.

我对css和JavaScript就不是很熟,很多东西都不会。开始写页面的时候,就想着一个简单的页面而已,不会太难。但是当你自己真的来写的时候,还是有点困难的。这个困难怎么说呢?。页面是写出来了。但是,但是。太TM丑了。虽然我也是实用主义者。但是还是看不下去。我其实想模仿着b站上一个up主做的。后面背景图片用的一个GIF图。这样更好看一点。但是我的GIF不能覆盖整个背景,只能覆盖一半。那怎么办?没办法,只能一半一半了。害!

页面好看不是评判一个网站的好坏的根本标准,但是一个恶心人的页面一定不是好页面。
——鲁迅

困难2.

页面写好了,就要准备功能了。我准备用python开始写爬虫了。按照正常程序来,打开网易云主页,轻轻按下F12,开始找评论的请求,以我的技术,没一会就找到了。一个叫get?csrf_token=的请求。打开一看,没错里面就是我要找的评论。

一切顺利!

是个post请求,我就慢慢的往下移动,看看这个post要的数据,但是当我看到这个请求数据,我就感觉不妙,有一股淡淡的"杀气"

这么长的东西,是个什么鬼???这直接给我整emo了。

没办法,只有求助搜索引擎了。一百度,嘿,还真有,还蛮多。在大佬们的博客畅游了好久之后。总算知道了网易云的评论加密方式了。由开始的直接直接懵逼,到后来慢慢有点头绪。先主要介绍怎么加密的吧。

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
  }//这个a的函数的就是随机生成一个16位的整数
   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()//b函数的意思就是给传入的参数进行aes加密
  }
   function c(a, b, c) {
       var d, e;
       return setMaxDigits(131),
       d = new RSAKeyPair(b,"",c),
       e = encryptedString(d, a)
  }//c函数和encSecKey生成有关
   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
  }//d就是给再来一次aes加密
   function e(a, b, d, e) {
       var f = {};
       return f.encText = c(a + e, b, d),
       f
  }
   window.asrsea = d,
window.ecnonasr = e
}();
(function() {
   var c7f = NEJ.P
    , eu7n = c7f("nej.g")
    , u7n = c7f("nej.j")
    , j7c = c7f("nej.u")
    , Tu9l = c7f("nm.x.ek");
   Tu9l.emj = {
       "色": "00e0b",
       "流感": "509f6",
       "这边": "259df",
       "弱": "8642d",
       "嘴唇": "bc356",
       "亲": "62901",
       "开心": "477df",
       "呲牙": "22677",
       "憨笑": "ec152",
       "猫": "b5ff6",
       "皱眉": "8ace6",
       "幽灵": "15bb7",
       "蛋糕": "b7251",
       "发怒": "52b3a",
       "大哭": "b17a8",
       "兔子": "76aea",
       "星星": "8a5aa",
       "钟情": "76d2e",
       "牵手": "41762",
       "公鸡": "9ec4e",
       "爱意": "e341f",
       "禁止": "56135",
       "狗": "fccf6",
       "亲亲": "95280",
       "叉": "104e0",
       "礼物": "312ec",
       "晕": "bda92",
       "呆": "557c9",
       "生病": "38701",
       "钻石": "14af6",
       "拜": "c9d05",
       "怒": "c4f7f",
       "示爱": "0c368",
       "汗": "5b7a4",
       "小鸡": "6bee2",
       "痛苦": "55932",
       "撇嘴": "575cc",
       "惶恐": "e10b4",
       "口罩": "24d81",
       "吐舌": "3cfe4",
       "心碎": "875d3",
       "生气": "e8204",
       "可爱": "7b97d",
       "鬼脸": "def52",
       "跳舞": "741d5",
       "男孩": "46b8e",
       "奸笑": "289dc",
       "猪": "6935b",
       "圈": "3ece0",
       "便便": "462db",
       "外星": "0a22b",
       "圣诞": "8e7",
       "流泪": "01000",
       "强": "1",
       "爱心": "0CoJU",
       "女孩": "m6Qyw",
       "惊恐": "8W8ju",
       "大笑": "d"
  };
   Tu9l.md = ["色", "流感", "这边", "弱", "嘴唇", "亲", "开心", "呲牙", "憨笑", "猫", "皱眉", "幽灵", "蛋糕", "发怒", "大哭", "兔子", "星星", "钟情", "牵手", "公鸡", "爱意", "禁止", "狗", "亲亲", "叉", "礼物", "晕", "呆", "生病", "钻石", "拜", "怒", "示爱", "汗", "小鸡", "痛苦", "撇嘴", "惶恐", "口罩", "吐舌", "心碎", "生气", "可爱", "鬼脸", "跳舞", "男孩", "奸笑", "猪", "圈", "便便", "外星", "圣诞"]
}//window.asrsea其中的一个参数,是个定值
)();
(function() {
   var c7f = NEJ.P
    , eu7n = c7f("nej.g")
    , u7n = c7f("nej.j")
    , j7c = c7f("nej.u")
    , Tu9l = c7f("nm.x.ek")
    , l7e = c7f("nm.x");
   if (u7n.be8W.redefine)
       return;
   window.GEnc = true;
   var bva4e = function(chr0x) {
       var m7f = [];
       j7c.bf8X(chr0x, function(chh0x) {
           m7f.push(Tu9l.emj[chh0x])
      });
       return m7f.join("")
  };
   var cha0x = u7n.be8W;
   u7n.be8W = function(Y8Q, e7d) {
       var i7b = {}
        , e7d = NEJ.X({}, e7d)
        , mh9Y = Y8Q.indexOf("?");
       if (window.GEnc && /(^|\.com)\/api/.test(Y8Q) && !(e7d.headers && e7d.headers[eu7n.BJ4N] == eu7n.FN3x) && !e7d.noEnc) {
           if (mh9Y != -1) {
               i7b = j7c.hh8Z(Y8Q.substring(mh9Y + 1));
               Y8Q = Y8Q.substring(0, mh9Y)
          }
           if (e7d.query) {
               i7b = NEJ.X(i7b, j7c.fQ7J(e7d.query) ? j7c.hh8Z(e7d.query) : e7d.query)
          }
           if (e7d.data) {
               i7b = NEJ.X(i7b, j7c.fQ7J(e7d.data) ? j7c.hh8Z(e7d.data) : e7d.data)
          }
           i7b["csrf_token"] = u7n.gU8M("__csrf");
           Y8Q = Y8Q.replace("api", "weapi");
           e7d.method = "post";
           delete e7d.query;
           var bKf7Y = window.asrsea(JSON.stringify(i7b), bva4e(["流泪", "强"]), bva4e(Tu9l.md), bva4e(["爱心", "女孩", "惊恐", "大笑"]));//进行加密
           e7d.data = j7c.cs8k({
               params: bKf7Y.encText,
               encSecKey: bKf7Y.encSecKey
          })
      }

这个看的代码看的肯定有点长,但是关键代码也不多,我都有注释。我第一次看到这个也是一脸懵,怎么还有[爱心", "女孩", "惊恐", "大笑"],这都是什么鬼?后来我才知道,这就是来迷惑你的。上面有个字典,每一个词语都对应几个字母。后面用的是字母来当参数加密。经过我的测试,这个window.asrsea后面两个参数都是定值,你们可以在window.asrsea设个断点,然后在控制台输出这些值看看。你会发现,bva4e(["流泪", "强"])是'010001',bva4e(Tu9l.md)是 00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7。bva4e(["爱心", "女孩", "惊恐", "大笑"])是0CoJUm6Qyw8W8jud。

那么现在就只剩下一个参数了,那就是JSON.stringify(i7b)了。你可以试着也在控制台输出一下。你可以会发现是'{"csrf_token":""}'。这个可能是断点的问题,你换个断点你就会发现。i7b是这个。但是你的可能不叫i7b。你自己找找。

{
      'csrf_token': "",
      'cursor': "-1",
      'offset': "0",
      'orderType': "1",
      'pageNo': "1",
      'pageSize': 20,
      'rid': "R_SO_4_1376142151",
      'threadId': "R_SO_4_1376142151"
  }

可以发现rid和threadId的R_SO_4_后面跟的都是歌曲id。csrf_token没啥用,就让他空着。唯一变的就是pageSize。它一直都是20的倍数。这样就好解决了。现在只要搞清楚怎么加密就行了。我们慢慢顺着往上找window.asrsea,发现window.asrsea等于一个叫d的函数再往上找,你会发现一个叫b的函数,再往上,你又会找到一个叫a和c的函数。b的功能就是对传入的参数进行AES加密。a的作用就是随机生成一个函数。c的函数和encSecKey生成有关。后面你会发现对于一个固定的i会有一个固定encSecKey。所以我们只要用一个i生成的encSecKey到时候就可以一直用了。那么加密就是,a生成一个16位随机的整数,d也就是window.asrsea,先用第一个和最后一个参数进行一次aes加密生成w,然后又用w作为参数和a生成的16整数进行二次aes加密生成params。这个网易云呀!!太可恶了,加密这么多,可折磨死我了。

那么下面就直接上代码吧

from base64 import b64encode
import requests,json



e = "010001"
f = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g = "0CoJUm6Qyw8W8jud"
i = "0hyFaCNAVzOIdoht"

url = 'https://music.163.com/weapi/comment/resource/comments/get?csrf_token='
headers = {
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
  }

def get_encSecKey():
  return "4022359ea3110bcd034e0160c3b89e5e172fd0110a3cf765d9f366d9fd09840a1f4a4705ac43719fdb8bfeb44d3b92334733061ad10942131184a4dfba0ac9d2cf867b8b6236523c1ca5f44c0d2d82c1c2665a3137a9241c7373539c1aa8e5e9bb9d33dafc764b5d76c2ab34fc94df85e27a934c8a603fa713f2cf38c2b7bbae"

def get_params(data): #data默认是json字符串
  first = enc_params(data,g)
  second = enc_params(first,i)
  return second

def to_16(data):
  pad = 16-len(data)%16
  data +=chr(pad) * pad
  return data

def enc_params(data,key): #加密过程
  iv = "0102030405060708"
  data = to_16(data)
  aes = AES.new(key=key.encode('utf-8'),IV=iv.encode('utf-8'),mode=AES.MODE_CBC) #创建加密器
  bs = aes.encrypt(data.encode('utf-8')) #加密
  return str(b64encode(bs),"utf-8") #转化成字符串


if __name__ == '__main__':

  page = int(input('请输入需要爬取的页数:'))
  print('开始爬虫!!!')
  for j in range(1,page+1):
      page_num = str(j*20)
      data = {
          'csrf_token': "",
          'cursor': "-1",
          'offset': "0",
          'orderType': "1",
          'pageNo': "1",
          'pageSize': page_num,
          'rid': "R_SO_4_1376142151",
          'threadId': "R_SO_4_1376142151"
      }

      response = requests.post(url,data={
          "params":get_params(json.dumps(data)),
          "encSecKey":get_encSecKey()
      },headers=headers)


      result = json.loads(response.content.decode('utf-8'))
    i = random.randint(0,20)
      print(result['data']['comments'][i]['content'])
   
  print('爬取完毕!!!')



困难3.

那就是JavaScript发送request请求一直不成功,百度后说是请求域的问题,有点麻烦。等下次有时间把这个解决了吧。

今天就到这里吧!

猜你喜欢

转载自blog.csdn.net/qq_59848320/article/details/120589130