七麦数据-analysis值计算过程

debug

参照文章爬虫JS逆向之-七麦数据的步骤进入下面的网站,打开控制台,选择榜单类型全部
在这里插入图片描述
点击网络可以看到,页面的发出了下面的请求,其中analysis参数是请求加密后的参数
[图片]
响应结果
[图片]

加密参数获取

加密部分主要是参数的analysis,下面说明analysis参数值的结果是怎么加密得到的:

  1. function(t){...}函数对请求做处理,最终要拿到加密后的e(即analysis)
    [图片]
    我把上面的请求配置函数f(t){...}复制下来方便解释
function beforeRequest(t) {
  try {
    var n;
    f ||
      F != s ||
      ((n = (0, i[Wt])(m)),
      (s = c[x][k][Pt] = -(0, i[Wt])(l) || +new z[W]() - a2 * n));
    var e,
      r = +new z[W]() - (s || H) - 1661224081041,
      a = [];
    return (
      void 0 === t[Zt] && (t[Zt] = {}),
      z[Z][i7](t[Zt])[M](function (n) {
        if (n == p) return !B;
        t[Zt][N2](n) && a[b](t[Zt][n]);
      }),
      (a = a[Ot]()[I1](_)), 
      (a = (0, i[jt])(a)), 
      (a = (a += v + t[Jt][T](t[Mt], _)) + (v + r) + (v + 3)), 
      (e = (0, i[jt])((0, i[qt])(a, d))),
      -B == t[Jt][j](p) &&
        (t[Jt] += (-B != t[Jt][j](Rn) ? Hn : Rn) + p + B1 + z[V1](e)),
      t
    );
  } catch (t) {}
}
  1. 可以看到:e = (0, i[jt])((0, i[qt])(a, d)),这里的参数0可以忽略,文章中说0表示代码执行的优先级,因此e=i[jt](i[qt](a, d)),这里的i[jt]指加密函数v(t)i[qt]指解密函数h(n,t)
// 加密函数 i[jt]
function v(t) {
  t = z[V1](t)[T](/%([0-9A-F]{2})/g, function (n, t) {
    return o(Y1 + t);
  });
  try {
    return z[Q1](t);
  } catch (n) {
    return z[W1][K1](t)[U1](Z1);
  }
}
// 解密函数 i[qt]
function h(n, t) {
  t = t || u();
  for (var e = (n = n[$1](_))[R], r = t[R], a = q1, i = H; i < e; i++)
    n[i] = o(n[i][a](H) ^ t[(i + 10) % r][a](H));
  return n[I1](_);
}
  1. 因此e的值也可写作:e=v(h(a,d)),下面分析参数ad的在走到第25行计算e结果时是如何计算得到的。

计算参数

  1. 从debug模式右侧的作用域里可以看到请求参数t:
    [图片]
  2. 这里再改一下请求配置函数beforeRequest,仅截取对计算analysis值有作用的代码,返回e加上了注释。a的计算主要是下面6个步骤
function beforeRequest(t) {
  // t: 请求配置config,请求参数:t.params
  try {
    var e,
      r = +new z[W]() - (s || H) - 1661224081041,
      //1. 赋值为空数组
      a = [];
    void 0 === t[Zt] && (t[Zt] = {});
      // 下面是一个forEach回调,遍历每个请求参数 t[Zt] = t.params ={brand: "all",country: "cn",device: "iphone",genre: "36"}
      z[Z][i7](t[Zt])[M](function (n) {
        if (n == p) return !B; // p='analysis', B=1
        // 2. 这个函数执行没找到,应该把参数映射成结果数组,即从t.params得到下面步骤3的a
        t[Zt][N2](n) && a[b](t[Zt][n]); 
      }),
      //3. a = ["all","cn","iphone","36"]
      a = a[Ot]()[I1](_); //数组sort后用空字符串join
      //4. a="36allcniphone"
      a = (0, i[jt])(a); //加密a,i[jt]即上面的函数v
      //5. a="MzZhbGxjbmlwaG9uZQ=="
      a = (a += v + t[Jt][T](t[Mt], _)) + (v + r) + (v + 3); //t[Jt]=t.url='/rank/indexPlus/brand_id/2', r=23597922830(时间戳ms) v="@#"
      //6. a="MzZhbGxjbmlwaG9uZQ==@#/rank/indexPlus/brand_id/2@#23597922830@#3"
      e = (0, i[jt])((0, i[qt])(a, d)); // d = "xyz517cda96abcd"
      return e;
    );
  } catch (t) {}
}
  1. 函数中用到了闭包65165中的变量d, p s,代码往上翻可以看到这几个变量的定义:
    在这里插入图片描述
  2. 复制下来简单变形后得到:
  d = i[zt](Rt, B);
  s = c[x][k][Pt]; //c为Vue实例,这个变量为Vue.prototype.difftime,根据组件有所不同,这里直接赋值为610
  p = (i[Bt](i[Ht](Gt), d),i[Bt](i[Ht](It), d),i[Bt](i[Ht](Dt), d),i[Bt](i[Ht]($t), d));

可以看到代码里包含了很多常量,如Zt, W, jt等,可以在debug时的作用域闭包中找到
在这里插入图片描述
4. 替换掉里面的常量,找到这些函数的定义(下面为了避免函数与变量重名,将替换后的函数名后均添加了f,如函数v名字为vf),
在这里插入图片描述
5. 得到如下代码:

function beforeRequest(t) {
  var d = "xyz517cda96abcd"; //d = i[zt](Rt, B) = yf("qimai@2022&Technology", 1) = "xyz517cda96abcd",
  s = 610;                  // s = c[x][k][Pt] = Vue.prototype.difftime,根据组件有所不同
                            // 这里我测试了,s不管取什么值,虽然加密后的analysis值都不一样,
                            //analysis结果的字符串仅-10~-5位不一样,但是都能拿到请求数据
  p = "analysis";           // p = (i[Bt](i[Ht](Gt), d),i[Bt](i[Ht](It), d),i[Bt](i[Ht](Dt), d),i[Bt](i[Ht]($t), d)) 
                            //   = (pf(lf("Jwo="),d),pf(lf("Jw0=", d),pf(lf("Jw8=", d),pf(lf(""GRcbWUhEChc="", d)) 
                            //   = "analysis"
  var v = "@#";             // v = i[Bt](i[Ht](Nt), d) = pf(lf("OFo="), d) = "@#"
  try {
    let e,
      r = +new Date() - (s || 0) - 1661224081041,
      a = [];
    void 0 === t.params && (t.params = {});
    Object.keys(t.params).forEach(function (n) {
      if (n == p) return !B;
      t.params.hasOwnProperty(n) && a.push(t.params[n]);
    });
    a = a.sort().join("");
    a = vf(a);
    a = (a += v + t.url.replace(t.baseURL, "")) + (v + r) + (v + 3);
    e = vf(hf(a, d));
    return e;
  } catch (t) {}
}
  1. 其中被替换的函数如下:
// 加密 i[jt]
function vf(t) {
  t = encodeURIComponent(t).replace(/%([0-9A-F]{2})/g, function (n, t) {
    return ofn("0x" + t);
  });
  try {
    return btoa(t);
  } catch (n) {
    return Buffer.from(t).toString("base64");
  }
}
// 解密 i[qt]
function hf(n, t) {
  // t = t || u();
  for (
    let e = (n = n.split("")).length, r = t.length, a = "charCodeAt", i = 0;
    i < e;
    i++
  ) {
    n[i] = ofn(n[i][a](0) ^ t[(i + 10) % r][a](0));
  }
  return n.join("");
}

function ofn(n) {
  (t = ""),
    [
      "66",
      "72",
      "6f",
      "6d",
      "43",
      "68",
      "61",
      "72",
      "43",
      "6f",
      "64",
      "65",
    ].forEach(function (n) {
      t += unescape("%u00" + n);
    });
  var t,
    e = t;
  return String[e](n);
}

/************************* 下面是计算d, p需要的函数 ********************/
// i[zt]
function yf(n, t, e) {
  for (var r = void 0 === e ? 2166136261 : e, a = 0, i = n.length; a < i; a++)
    r =
      (r ^= n.charCodeAt(a)) +
      ((r << 1) + (r << 4) + (r << 7) + (r << 8) + (r << 24));
  return t ? ("xyz" + (r >>> 0).toString(16) + "abcd").substr(-16) : r >>> 0;
}

// i[Bt]
function pf(n, t) {
  // t = t || u();
  for (
    let e = (n = n.split("")).length, r = t.length, a = "charCodeAt", i = 0;
    i < e;
    i++
  )
    n[i] = String.fromCharCode(n[i][a](0) ^ t[i % r][a](0));
  return n.join("");
}
// i[Ht]
function lf(n) {
  return decodeURIComponent(
    (function (t) {
      try {
        return atob(t);
      } catch (n) {
        return z["Buffer"]["from"](t, "base64").toString();
      }
    })(n)
      .split("")
      .map(function (n) {
        return "%" + ("00" + n.charCodeAt(0).toString(16)).slice(-2);
      })
      .join("")
  );
}

完整代码


// 加密 i[jt]
function vf(t) {
  t = encodeURIComponent(t).replace(/%([0-9A-F]{2})/g, function (n, t) {
    return of("0x" + t);
  });
  try {
    return btoa(t);
  } catch (n) {
    return Buffer.from(t).toString("base64");
  }
}
// 解密 i[qt]
function hf(n, t) {
  // t = t || u();
  for (
    let e = (n = n.split("")).length, r = t.length, a = "charCodeAt", i = 0;
    i < e;
    i++
  ) {
    n[i] = of(n[i][a](0) ^ t[(i + 10) % r][a](0));
  }
  return n.join("");
}

function of(n) {
  (t = ""),
    [
      "66",
      "72",
      "6f",
      "6d",
      "43",
      "68",
      "61",
      "72",
      "43",
      "6f",
      "64",
      "65",
    ].forEach(function (n) {
      t += unescape("%u00" + n);
    });
  var t,
    e = t;
  return String[e](n);
}

/************************* 下面是计算d, p需要的函数 ********************/
// i[zt]
function yf(n, t, e) {
  for (var r = void 0 === e ? 2166136261 : e, a = 0, i = n.length; a < i; a++)
    r =
      (r ^= n.charCodeAt(a)) +
      ((r << 1) + (r << 4) + (r << 7) + (r << 8) + (r << 24));
  return t ? ("xyz" + (r >>> 0).toString(16) + "abcd").substr(-16) : r >>> 0;
}

// i[Bt]
function pf(n, t) {
  // t = t || u();
  for (
    let e = (n = n.split("")).length, r = t.length, a = "charCodeAt", i = 0;
    i < e;
    i++
  )
    n[i] = String.fromCharCode(n[i][a](0) ^ t[i % r][a](0));
  return n.join("");
}
// i[Ht]
function lf(n) {
  return decodeURIComponent(
    (function (t) {
      try {
        return atob(t);
      } catch (n) {
        return z["Buffer"]["from"](t, "base64").toString();
      }
    })(n)
      .split("")
      .map(function (n) {
        return "%" + ("00" + n.charCodeAt(0).toString(16)).slice(-2);
      })
      .join("")
  );
}

function beforeRequest(t) {
  var d = "xyz517cda96abcd"; //d = i[zt](Rt, B) = yf("qimai@2022&Technology", 1) = "xyz517cda96abcd",
  s = 610;                  // s = c[x][k][Pt] = Vue.prototype.difftime,根据组件有所不同,这里测试的页面为610
  p = "analysis";           // p = (i[Bt](i[Ht](Gt), d),i[Bt](i[Ht](It), d),i[Bt](i[Ht](Dt), d),i[Bt](i[Ht]($t), d)) 
                            //   = (pf(lf("Jwo="),d),pf(lf("Jw0=", d),pf(lf("Jw8=", d),pf(lf(""GRcbWUhEChc="", d)) 
                            //   = "analysis"
  var v = "@#";             // v = i[Bt](i[Ht](Nt), d) = pf(lf("OFo="), d) = "@#"
  try {
    let e,
      r = +new Date() - (s || 0) - 1661224081041,
      a = [];
    void 0 === t.params && (t.params = {});
    Object.keys(t.params).forEach(function (n) {
      if (n == p) return !B;
      t.params.hasOwnProperty(n) && a.push(t.params[n]);
    });
    a = a.sort().join("");
    a = vf(a);
    a = (a += v + t.url.replace(t.baseURL, "")) + (v + r) + (v + 3);
    e = vf(hf(a, d));
    return e;
  } catch (t) {}
}
// 输入请求参数
let analysis = beforeRequest({
  url: "/app/comment",
  baseURL: "https://api.qimai.cn",
  params: { appid: "1658934469", country: "cn", page: 1 },
});

console.log(analysis);

运行结果:
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
爬虫(Web Crawler)是一种自动化程序,用于从互联网上收集信息。其主要功能是访问网页、提取数据并存储,以便后续分析或展示。爬虫通常由搜索引擎、数据挖掘工具、监测系统等应用于网络数据抓取的场景。 爬虫的工作流程包括以下几个关键步骤: URL收集: 爬虫从一个或多个初始URL开始,递归或迭代地发现新的URL,构建一个URL队列。这些URL可以通过链接分析、站点地图、搜索引擎等方式获取。 请求网页: 爬虫使用HTTP或其他协议向目标URL发起请求,获取网页的HTML内容。这通常通过HTTP请求库实现,如Python中的Requests库。 解析内容: 爬虫对获取的HTML进行解析,提取有用的信息。常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质中,以备后续分析或展示。常用的存储形式包括关系型数据库、NoSQL数据库、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫需要遵守网站的robots.txt协议,限制访问频率和深度,并模拟人类访问行为,如设置User-Agent。 反爬虫应对: 由于爬虫的存在,一些网站采取了反爬虫措施,如验证码、IP封锁等。爬虫工程师需要设计相应的策略来应对这些挑战。 爬虫在各个领域都有广泛的应用,包括搜索引擎索引、数据挖掘、价格监测、新闻聚合等。然而,使用爬虫需要遵守法律和伦理规范,尊重网站的使用政策,并确保对被访问网站的服务器负责。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值