原来python还可以这么玩python逆向爬取网易云评论进行情感分析

Posted 夜斗小神社

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原来python还可以这么玩python逆向爬取网易云评论进行情感分析相关的知识,希望对你有一定的参考价值。

遥遥微光,与我同行


好久不见,各位小伙伴们!嗐,春节真滴快啊!祝大家新年快乐!

书山有路勤为径,学海无涯苦作舟!又得开始愉快滴学习了!

小夜斗今天给大家伙分享一期干货,芜湖起飞!

JS逆向网易云爬取评论并利用snownpl进行情感分析

一:逆向破解网易云参数抓取评论信息

网易云PC端url: https://music.163.com/#/song?id=1817702136

要抓取滴评论如下图所示:


老规矩,检查网页元素,找到评论信息所在的请求网址!

从xhr里面找一下子就能找到,看下面截图:


如果直接请求这个网址的话,是拿不到上面的评论信息的,因为这个网址有两个动态加密的参数:paramsencSecKey


请求这个方有评论信息的url,我们需要上述两个参数构建表单发送POST请求,现在我们需要做的事情就是探索这两个参数是如何生产的,最后拿到它俩,构造自己的表单,发送POST请求获取响应!

第一个办法: 分析网页源码,找到生产参数所需要的方法,利用网页自身的代码拿到两个参数即可!(一般是分析javascript代码,俗称js逆向)

第二个办法: 了解这个网页js代码如何生产的这两个参数,并利用python仿写js代码所具有的功能,自己构造俩个参数!

我们先从一大堆请求中找到带有 paramsencSecKey参数文件

按下ctrl + F 搜索params, 找到箭头所指的2文件!


点击源代码进入js文件,并点击格式化js代码,格式化后如图二

图一:

图二:格式化后输入params找到其位置,一步步分析如何生产


好啦,到了最关键的地步,逆向分析这俩参数是如何加密产生的!

第一步, 找到生成这两个参数的js代码,如下所示:

把js代码扣下来看:

e5j.data = j5o.cr6l(
                params: bWv4z.encText,
                encSecKey: bWv4z.encSecKey
            )
        

看起来是’bWv4z’这个对象调用encTextencSecKey这两个方法分别生产的paramsencSecKey

第二步: 然后我们找找 bWv4z 对象是怎么生成的

var bWv4z = window.asrsea(JSON.stringify(i5n), bsK6E(["流泪", "强"]), bsK6E(XR1x.md), bsK6E(["爱心", "女孩", "惊恐", "大笑"]));

一个window.asrsea对象中传入4个参数生成bWv4z对象,其实这个时候可以先分析传入的四个参数是什么,或者先找到window.asrsea对象是如何生产的,这里我们先看后者

第三步: window.asrsea是如何产生的


通过上图我们知道,这个对象是由 d 产生的,一开始小夜斗也不知道d是个什么东西,通过搜索后发现,d是一个方法, 如下图所示:


这我们就知道了,window.asrsea相当于是d方法赋值(不太懂js代码,小夜斗自己是这么理解的),然后window.asrsea传入的四个参数就相当于调用d中需要传入的四个参数!

让我们打个断点看看,按下f5刷新页面,看看d中传入的四个参数!


d函数中四个参数如下图所示:

d: ""csrf_token":"d4339865ec133c9a7d77a25389bc0265""
e: "010001"
f: "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g: "0CoJUm6Qyw8W8jud"

我们再来看一下window.asrsea 其中四个参数分别是上述: d e f g

var bWv4z = window.asrsea(JSON.stringify(i5n), bsK6E(["流泪", "强"]), bsK6E(XR1x.md), bsK6E(["爱心", "女孩", "惊恐", "大笑"]));

其中呢我们看第一个参数JSON.stringify(i5n)对应的是d,大概就是将i5n转化为json格式吧,我们打个断点看看最后i5n是什么!

i5n = csrf_token: "d4339865ec133c9a7d77a25389bc0265"
// 上下对比发现JSON.stringify(i5n)是将i5n转化为json格式
d: ""csrf_token":"d4339865ec133c9a7d77a25389bc0265""

小夜斗换了一首歌发现,上面这四个参数都是固定的:

url: https://music.163.com/#/song?id=1820887593

d: ""csrf_token":"d4339865ec133c9a7d77a25389bc0265""
e: "010001"
f: "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g: "0CoJUm6Qyw8W8jud"

重新打断点,选择第二页后有了新的发现!

注意,打第二页断点的时候,要先打断点,f5刷新后会跳转到第一页,之个时候你在选择第二页,就会加载参数内容了!

d: ""rid":"R_SO_4_1817702136","threadId":"R_SO_4_1817702136","pageNo":"2","pageSize":"20","cursor":"1613900247044","offset":"40","orderType":"1","csrf_token":"d4339865ec133c9a7d77a25389bc0265""
e: "010001"
f: "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g: "0CoJUm6Qyw8W8jud"

让我们看看d: 里面几个参数的含义

“rid”:“R_SO_4_1817702136” 后面这个数字是网页url后面的id (根据id变换)
“threadId”:“R_SO_4_1817702136” 同上 (根据id 变换)
“pageNo”:“2” 页码数 (变量)
“pageSize”:“20” 每一页评论的数量 常量
“cursor”:“1613900247044” 应该是时间戳13位 (变量)
“offset”:“40” 偏移量 (页码数 * 20) (变量)
“orderType”:“1” 估计是啥类型是个常量
“csrf_token”:“d4339865ec133c9a7d77a25389bc0265” 同样是个常量

好勒,了解了四个参数后我们可以看看d函数内部到底做了啥事情!

第四步: d函数内部到底做了啥事情!

把js代码扣下来如下所示:

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
    

定义了一个字典h, 变量i的值是a(16)

了解后发现,a、b、c、d都是函数


首先看看函数a内部:

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
    

乍一看,我滴天这是啥子东西,别急我们用pycharm来执行这个js代码即可知道返回的c是个什么东西,即可倒推这个函数的功能得到i

因为将网页源码的js代码copy到pycharm里面执行会因为某些换行符报错,小夜斗就将js代码copy到了下面这个软件: 好像是前端用滴!

pycharm中执行js文件的代码如下:

pip install PyExecJS  # 安装执行js代码的库
#-*- coding: utf-8 -*-

# TODO: 正确的js代码里面(网页上copy的有换行符)
# 解决办法,先将代码copy到HBUILD里面, 然后执行js代码

import execjs
import requests
js = open('./analysis_3.js', 'r', encoding='utf8').read()
aim = execjs.compile(js)  # 生产js对象
data = aim.call('example')  # 调用相应方法
print(data)  # 输出结果

结果如下所示: 每次生产不一样的长度为16的字符串!估计就是从a函数那个很长的字符串中随机选择16个字符串然后拼接在一起吧,小夜斗猜测这就是a函数的功能!


结果2如下图所示:


下面是第一次为了获得变量i值扣下来的js代码(analysis_3.js):

/*
d: ""rid":"R_SO_4_1817702136","threadId":"R_SO_4_1817702136","pageNo":"2","pageSize":"20","cursor":"1613900247044","offset":"40","orderType":"1","csrf_token":"d4339865ec133c9a7d77a25389bc0265""
e: "010001"
f: "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g: "0CoJUm6Qyw8W8jud"
 */

function d(d, e, f, g) 
        var h = 
          , i = a(16);
        return h.encText = b(d, g), // 执行到此已经获取到变量i的值
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
    
    

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 example()
	i = a(16);
	return i;
	


然后我们再回到d函数内部,执行代码h.encText = b(d, g),即我们需要调用b函数,其中两个参数分别为d,g,这俩参数我们都能构造的知,问题不大!继续扣js代码!

先将函数b扣下来看看:

/*
d: ""rid":"R_SO_4_1817702136","threadId":"R_SO_4_1817702136","pageNo":"2","pageSize":"20","cursor":"1613900247044","offset":"40","orderType":"1","csrf_token":"d4339865ec133c9a7d77a25389bc0265""
e: "010001"
f: "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g: "0CoJUm6Qyw8W8jud"
 */

function d(d, e, f, g) 
        var h = 
          , i = a(16);
        return h.encText = b(d, g), // 执行到此已经获取到变量i的值
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
    
    

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 example()
	i = a(16);
	h = 
	
	h.encText = b("rid":"R_SO_4_1817702136","threadId":"R_SO_4_1817702136","pageNo":"2","pageSize":"20","cursor":"1613900247044","offset":"40","orderType":"1","csrf_token":"d4339865ec133c9a7d77a25389bc0265", "0CoJUm6Qyw8W8jud")
	return h;


从Pycharm执行这个js文件发现报错: CryptoJS is not defined


就是js代码中少了**CryptoJS **这个函数功能,问题不大我们从js源码中扣下来即可!就搜这个函数名字,然后找到看起来像这个函数复制下来即可!不难!


第三次扣下来的js代码如下所示:

/*
d: ""rid":"R_SO_4_1817702136","threadId":"R_SO_4_1817702136","pageNo":"2","pageSize":"20","cursor":"1613900247044","offset":"40","orderType":"1","csrf_token":"d4339865ec133c9a7d77a25389bc0265""
e: "010001"
f: "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g: "0CoJUm6Qyw8W8jud"
 */

function d(d, e, f, g) 
        var h = 
          , i = a(16);
        return h.encText = b(d, g), // 执行到此已经获取到变量i的值
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
    
    

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()
    
var CryptoJS = CryptoJS || function(u, p) 
    var d = 
      , l = d.lib = 
      , s = function() 
      , t = l.Base = 
        extend: function(a) 
            s.prototype = this;
            var c = new s;
            a && c.mixIn(a);
            c.hasOwnProperty("init") || (c.init = function() 
                c.$super.init.apply(this, arguments)
            
            );
            c.init.prototype = c;
            c.$super = this;
            return c
        ,
        create: function() 
            var a = this.extend();
            a.init.apply(a, arguments);
            return a
        ,
        init: function() ,
        mixIn: function(a) 
            for (var c in a)
                a.hasOwnProperty(c) && (this[c] = a[c]);
            a.hasOwnProperty("toString") && (this.toString = a.toString)
        ,
        clone: function() 
            return this.init.prototype.extend(this)
        
    
      , r = l.WordArray = t.extend(
        init: function(a, c) 
            a = this.words = a || [];
            this.sigBytes = c != p ? c : 4 * a.length
        ,
        toString: function(a) 
            return (a || v).stringify(this)
        ,
        concat: function(a) 
            var c = this.words
              , e = a.words
              , j = this.sigBytes;
            a = a.sigBytes;
            this.clamp();
            if (j % 4)
                for (var k = 0; k < a; k++)
                    c[j + k >>> 2] |= (e[k >>> 2] >>> 24 - 8 * (k % 4) & 255) << 24 - 8 * ((j + k) % 4);
            else if (65535 < e.length)
                for (k = 0; k < a; k += 4)
                    c[j + k >>> 2] = e[k >>> 2];
            else
                c.push.apply(c, e);
            this.sigBytes += a;
            return this
        ,
        clamp: function() 
            var a = this.words
              , c = this.sigBytes;
            a[c >>> 2] &= 4294967295 << 原来Python爬虫还可以这么玩!python爬虫自动化实现B站自动登录

原来Python爬虫还可以这么玩!python爬虫自动化实现B站自动登录

原来Python爬虫还可以这么玩!python爬虫自动化实现B站自动登录

酸爽!原来IDEA还可以这么玩 MyBatis,让编码速度飞起

打码平台保姆级使用教材夜斗小神社打卡滴~回家

爬取网贷之家的数据