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

前言

在这里插入图片描述
网络爬虫的大障碍,就是各种加密。这其中包过登录的验证码以及加密。js混淆、js参数加密等等。其实以前也就了解过js加密。但是没有深入研究,借着这次实践研究了一下网易云音乐的加密方式。

博主通过网易云音乐评论加密的实例来做个学习过程的分析和分享。

如果有问题或者不懂的地方可以关注我的微信公众号(bigsai),联系我。

技能点

  • 前端:js知识(比较重要)、谷歌浏览器debug、抓包、打断点调试能力(必须)。以及js各种加密函数(了解).
  • python:基础的请求requestsCrypto.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)看与其它语言的约定,有的会使用'\0'
    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)

在这里插入图片描述
源码github地址求Star。

如果有疑问或者过期可以关注我公众号联系我。

  • 欢迎关注我的个人公众号:bigsai
  • 发布了192 篇原创文章 · 获赞 1770 · 访问量 61万+

    没有更多推荐了,返回首页

    ©️2019 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503

    分享到微信朋友圈

    ×

    扫一扫,手机浏览