地址: https://www.qimai.cn/rank
逆向的目标是请求参数:

全局搜索:第三个文件不用看了,因为是CSS文件。

看到下面的请求的是URL有所搜索的关键字,而关键字是来自请求参数的。好像不太对。所以搜索 analysis 没啥用,要用其他方案搜索。

观察到每个请求都有analysis,说明所有请求都要过一个地方,根据经验,应该是axios,应该是一个拦截器做的事。
观察一下启动器,用到了promise。
所以应该搜索 interceptors

看到下面,这是源码

python代码:
import subprocess
from functools import partial
subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")
import execjs
import requests
import json
f = open("七麦.js", mode='r', encoding="utf-8")
js_code = f.read()
f.close()
js = execjs.compile(js_code)
url = "https://api.qimai.cn/rank/index"
params = {
"brand": "free",
"device": "iphone",
"country": "cn",
"genre": "36",
"date": "2023-05-15",
"page": 3,
"is_rank_index": 1,
"snapshot": "23:08:06"
}
analysis = js.call("fn", url, params)
params['analysis'] = analysis
resp = requests.get(url, params, headers={
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
})
print(resp.json())
七麦.js
// # 所有请求都有该参数.
// # 用的应该是axios
// # 参数应该是在axios的拦截器上进行的加密
// # 拦截器搜不到...
// haha = 'interc'+'eptors'
//
// axios[haha]['request']["use"](function(){}, function(){})
//
// axios.interceptors.response.use(function(){}, function(){})
// 猜测. 网站的逻辑应该是用上述逻辑完成的.
// 遇到这种情况怎么办?
// 从axios源码下手....
/**
axios.interceptors.request => 里面有个[] <= req_lst
axios.interceptors.request.use(请求前resolve1, 请求前reject1)
axios.interceptors.request.use(function(){}, function(){})
会改变req_lst
[{fulfilled: function(){}, rejected: function(){}}]
axios.interceptors.response => 里面有个[] <= resp_lst
axios.interceptors.response.use(响应后resolve1, 响应后reject1)
会改变resp_lst
[{fulfilled: function(){}, rejected: function(){}}]
var t = [请求前resolve1, 请求前reject1, 发请求, undefined, 响应后resolve1, 响应后reject1];
// 把请求拦截器中所有的对象: {fulfilled, rejected}循环一遍
this.interceptors.request.forEach((function(e) {
t.unshift(e.fulfilled, e.rejected)
}
));
this.interceptors.response.forEach((function(e) {
t.push(e.fulfilled, e.rejected)
}
));
for ( ;t.length; ){
n = n.then(响应后resolve2, 响应后reject2);
debugger; => t => [请求拦截器, 发请求, undefined, 响应拦截器的东西]
n = n.then(t.shift(), t.shift());
}
return n
for(第一部分;第二部分;第三部分){
}
*/
// global就是node的全局
window = global;
function o(n) {
t = "",
['66', '72', '6f', '6d', '43', '68', '61', '72', '43', '6f', '64', '65']['forEach'](function(n) {
t += window['unescape']('%u00' + n)
});
var t, e = t;
return window['String']["fromCharCode"](n)
}
function v1(t) {
t = window['encodeURIComponent'](t)["replace"](/%([0-9A-F]{2})/g, function(n, t) {
return o("0x" + t)
});
try {
return window["btoa"](t); // base64 浏览器的
} catch (n) {
return window["Buffer"]["from"](t)["toString"]("base64"); // base64 nodejs的
}
}
function h(n, t) {
for (var e = (n = n["split"](""))["length"], r = t["length"], a = "charCodeAt", i = 0; i < e; i++)
n[i] = o(n[i][a](0) ^ t[(i + 10) % r][a](0));
return n["join"]("")
}
function fn(url, params) {
let baseURL = 'https://api.qimai.cn'
let s = -56;
var e, r = +new Date - (s) - 1661224081041, a = [];
void 0 === params && (params = {}),
// 循环参数. 然后把参数的值. 塞入 数组a中
Object.keys(params)["forEach"](function(n) {
if (n === "analysis")
return !1;
params["hasOwnProperty"](n) && a["push"](params[n])
}),
a = a["sort"]()["join"](""),
a = v1(a),
a = (a += "@#" + url["replace"](baseURL, "")) + ("@#" + r) + ("@#" + 3);
e = v1(h(a, "xyz517cda96abcd"));
return e;
}