注:本文为转载,转载自思不歌qwq

目录

python爬虫简单js逆向案例

一、找到包含所需数据的ajax数据包

二、通过浏览器工具进行关键字定位

三、分析相关js文件,找出具体实现方式

1、getApiKey()函数

2、encryptApiKey()函数

3、encryptTime()函数

4、comb()函数

5、查找不一致的原因

6、将上述js代码改写成python代码


python爬虫简单js逆向案例

由于学习任务需要用爬虫获取数据,学习了python爬虫的基础知识。
但是在开始写爬虫程序的时候就出现了问题,现将解决方式记录如下。

内容简介

需求:爬取某金融区块链网站 https://www.oklink.com/btc/tx-list的数据。

出现问题:爬取数据首先需要发送请求,得到响应数据 。通过网页分析可知,需要获取的数据来自ajax动态加载,所以我选择通过向ajax的 url 发送请求得到响应数据。请求的请求头中参数是需要解决的首要问题。但是请求到数据的ajax请求头中包含类似如下动态变化且加密的参数
 

xapiKey:LWIzMWUtNDU0Ny05Mjk5LWI2ZDA3Yjc2MzFhYmEyYzkwM2NjfDI3MTk0ODY0MDUwNzA2Mjk=

解决办法:通过分析浏览器请求到的相关js文件,得到x-apiKey的生成函数,并用python复写(可以不用复写,通过调用python中相关的js库也可执行js代码)。

一、找到包含所需数据的ajax数据包

1.打开网站,快捷键Ctrl+shift+I打开浏览器工具,选择Network->XHR,刷新网页,可以看到只有一个请求数据包:

2.打开之后可以找到请求的url: 

 https://www.oklink.com/api/explorer/v1/btc/transactionsNoRestrict?t=1608475589424&limit=20&offset=0

 3.对ajax的url中参数的解释:

 1. get请求
 2. t=1608475589424 为 时间戳
 3. limit=20 为每页的交易数量
 4. offset=0 为每页的起始交易位置

4.在下面可以找到请求头中包含的参数:

 5.其中的x-apiKey就是请求头中的加密参数,而且每次刷新都会变化。

x-apiKey: LWIzMWUtNDU0Ny05Mjk5LWI2ZDA3Yjc2MzFhYmEyYzkwM2NjfDI3MTk1ODY3MDA1MzExODU=

二、通过浏览器工具进行关键字定位

1.在右上方找到Search:

2.输入:x-apiKey,查询到三个结果,如图

3.可以看到第三个js文件后包含header,打开它。
这里可以点击下面的 {} 将代码格式化:

4.按快捷键Ctrl+F再次在该js文件里面查找x-apiKey
定位到x-apiKey之后,将相关联的代码段取出:

function(t, e, n) {
    "use strict";
    n(115),
    n(57),
    n(20),
    n(60);
    function r(t, e) {
        for (var n = 0; n < e.length; n++) {
            var r = e[n];
            r.enumerable = r.enumerable || !1,
            r.configurable = !0,
            "value"in r && (r.writable = !0),
            Object.defineProperty(t, r.key, r)
        }
    }
    var o = new (function() {
        function t() {
            !function(t, e) {
                if (!(t instanceof e))
                    throw new TypeError("Cannot call a class as a function")
            }(this, t),
            this.API_KEY = "a2c903cc-b31e-4547-9299-b6d07b7631ab"
        }
        return function(t, e, n) {
            e && r(t.prototype, e),
            n && r(t, n)
        }(t, [{
            key: "encryptApiKey",
            value: function() {
                var t = this.API_KEY
                  , e = t.split("")
                  , n = e.splice(0, 8);
                return t = e.concat(n).join("")
            }
        }, {
            key: "encryptTime",
            value: function(t) {
                var e = (1 * t + 1111111111111).toString().split("")
                  , n = parseInt(10 * Math.random(), 10)
                  , r = parseInt(10 * Math.random(), 10)
                  , o = parseInt(10 * Math.random(), 10);
                return e.concat([n, r, o]).join("")
            }
        }, {
            key: "comb",
            value: function(t, e) {
                var n = "".concat(t, "|").concat(e);
                return window.btoa(n)
            }
        }, {
            key: "getApiKey",
            value: function() {
                var t = (new Date).getTime()
                  , e = this.encryptApiKey();
                return t = this.encryptTime(t),
                this.comb(e, t)
            }
        }]),
        t
    }())
      , i = window.utils.ont
      , c = Object.assign({}, i);
    c.interceptors.request.use(function(t) {
        return t.url.indexOf("api/explorer/v1") > -1 && (t.headers.common["x-apiKey"] = o.getApiKey()),
        t
    });
    e.a = c
}

三、分析相关js文件,找出具体实现方式

很明显,x-apiKey是变量o通过调用getApiKey()函数得到

t.headers.common["x-apiKey"] = o.getApiKey()

1、getApiKey()函数

找到函数定义:

key: "getApiKey",
value: function() {
	var t = (new Date).getTime()
 	  , e = this.encryptApiKey();
	return t = this.encryptTime(t),
	this.comb(e, t)
	}
  1. 变量 t 是获得当前时间,变量 e 是调用encryptApiKey()得到的;
  2. 之后将变量 t 作为参数传递给encryptTime()函数 ,得到新的变量 t ;
  3. 最后将变量 t 和变量 e 作为参数传递给comb()函数,获得最终返回值x-apiKey。

2、encryptApiKey()函数

key: "encryptApiKey",
value: function() {
	var t = this.API_KEY
      , e = t.split("")
      , n = e.splice(0, 8);
   return t = e.concat(n).join("")
   }

1.变量 t 是一个固定字符串,从上面可以找到

this.API_KEY = "a2c903cc-b31e-4547-9299-b6d07b7631ab"

2.变量 e 是 对变量 t 进行切片,得到 t 里的每个字符,变量 n 得到 e 里的前8个字符,即:

"a", "2", "c", "9", "0", "3", "c", "c"

3.最后的变量 t 是 将 e 去掉 n 部分并在后面接上 n ,得到新的字符串返回值

"-b31e-4547-9299-b6d07b7631aba2c903cc"

该值即为getApiKey()函数中comb()函数的 e 参数。

3、encryptTime()函数

key: "encryptTime",
value: function(t) {
	var e = (1 * t + 1111111111111).toString().split("")
      , n = parseInt(10 * Math.random(), 10)
      , r = parseInt(10 * Math.random(), 10)
      , o = parseInt(10 * Math.random(), 10);
	return e.concat([n, r, o]).join("")
	}
  • t 是传入的当前时间(13位的时间戳),经过字符串处理交给e;

  • 变量 n , r , o 是产生的0-10的一个随机整数,并结合 e 生成新的关于当前时间和3个随机数的新字符串,比如

"2", "7", "1", "9", "4", "9", "2", "4", "3", "8", "8", "9", "9"]

该值即为getApiKey()函数中comb()函数的 t 参数。

4、comb()函数

key: "comb",
value: function(t, e) {
	var n = "".concat(t, "|").concat(e);
	return window.btoa(n)
    }

1.变量 n 为传入参数 t 和 e 处理后得到

2719492921233667|-b31e-4547-9299-b6d07b7631aba2c903cc

2.window.btoa(n)为base64加密函数,得到加密后的 n

 MjcxOTQ5MzI5ODc0MjA2NXwtYjMxZS00NTQ3LTkyOTktYjZkMDdiNzYzMWFiYTJjOTAzY2M=

3.对比浏览器请求头中的x-apiKey

LWIzMWUtNDU0Ny05Mjk5LWI2ZDA3Yjc2MzFhYmEyYzkwM2NjfDI3MTk0ODc1Mzk0ODMwOTE=

可以看到有较为明显的不一致

5、查找不一致的原因

1.将在浏览器中得到的x-apiKey通过base64解码得到

-b31e-4547-9299-b6d07b7631aba2c903cc|2719487539483091

2.而通过上述过程得到的加密前的字符串是

2719492921233667|-b31e-4547-9299-b6d07b7631aba2c903cc

3.可以轻易观察到,我们自己生成的加密前的数据和正确的数据的区别是 " | " 前后的字符串顺序;

4.经过修改顺序,得到正确结果。

6、将上述js代码改写成python代码

# 获取动态变化且加密的x-apiKey
def get_x_apikey():
    # API_KEY固定字符串
    API_KEY = "a2c903cc-b31e-4547-9299-b6d07b7631ab"
    Key1 = API_KEY[0:8]
    Key2 = API_KEY[8:]
    #  交换API_KEY部分内容
    new_Key = Key2 + Key1
    # 获取当前时间,毫秒级
    cur_time = round(time.time() * 1000)
    # 处理获得的时间
    new_time = str(1 * cur_time + 1111111111111)
    # 生成三个0-9的随机整数
    random1 = str(random.randint(0, 9))
    random2 = str(random.randint(0, 9))
    random3 = str(random.randint(0, 9))
    # 再次处理时间字符串
    cur_time = new_time + random1 + random2 + random3
    # 将包含API_KEY和时间串的内容合并
    this_Key = new_Key + '|' + cur_time
    # 转码
    n_k = this_Key.encode('utf-8')
    # base64加秘
    x_apiKey = base64.b64encode(n_k)
    # 将加密后的x_apiKey返回
    return str(x_apiKey, encoding='utf8')

至此就成功解决了这个js逆向的工作。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐