页面 url:https://ec.minmetals.com.cn/logonAction.do

image.png

逆向

这里抓包到几个请求,其中有个 public,这个点进去才也得猜公钥,或者结合后面的猜。然后有个 by-lx-page,这个显然就是数据了。

image.png

看到了 Promise.then,首先就要想到拦截器,搜索结果如下:

image.png
image.png

搜到这里就可以确定没有使用了。

接着尝试搜参数:

image.png

太多了,一个一个看不现实。

最后只能搜 url 了:

image.png

成功!

点进去,可以看到这里传入的就是 post 的负载参数:

image.png

image.png

这里和上次一样是一个异步请求,调试到这里 t 已经被加密了,所以去上一步找,也就是 return A.next=2,y(e),返回的是 y(e) 的值,点进去:

image.png

这里就可以看到,它先获取“公钥”然后进行加密:

image.png

这里就是非堆成加密的第二种形式:[[关于非对称加密]]

接下来就可以写加密过程了,但是有个需要注意的地方,这里这个函数:

image.png

点进去也不知道是啥,而且他这里调用 B() 返回一个函数,这个函数会对 e 做反序列化,所以是有必要知道这里干了啥的,所以只能使用控制台了:

image.png

调用控制台之后,这里显示他是个 md5之类的函数,测试一下:

image.png

测试结果可以看出来,这里其实是做了一个 md5校验。
另外有个经验性的事情:这里是一个给字典赋值的操作, sign 关键词也就是签名,所以从经验的顺序可以猜测:

md5 -> sha128 -> sha256 -> sha512

经过上面的分析,逆向过程就可以写出来了:

var JSEncrypt = require("node-encrypt-js")  
var crypto = require("crypto")  

function my_md5(e) {  
    return crypto.createHash("md5").update(e).digest("hex")  
}  

function fn1() {  
    console.log(a)  

    var a = 1  
}  

// pub: 公钥  
// ming: 明文字典  

function fn(e, r) {  
    var t = new JSEncrypt()  
    t.setPublicKey(r)  

    e.sign = my_md5(JSON.stringify(e))  
    e.timeStamp = new Date().getTime()  

    var s = t.encryptLong(JSON.stringify(e))  

    return JSON.stringify({  
        param: s  
    })  
}

爬虫

孩子们,这里有大坑:

  1. 他在请求 public 的时候 Content-Length 为0,但是后面改了
  2. 请求数据的时候添加了一个头:Content-Type:application/json

image.png

完整代码:

import requests  
import json  

headers = {  
    "Accept": "application/json, text/plain, */*",  
    "Accept-Encoding": "gzip, deflate, br",  
    "Accept-Language": "zh-CN,zh;q=0.9",  
    "Cache-Control": "no-cache",  
    "Connection": "keep-alive",  
    # "Content-Length": "0",  
    "content-type": "application/json",  
    "Cookie": "SUNWAY-ESCM-COOKIE=6d8e7dba-10be-43c6-8787-e4e7c1d253c0; JSESSIONID=1023098F4479F4F271AE84F13CBD7C43; __jsluid_s=85275cda5c30c5dab166ffcf0a5308ac",  
    "Host": "ec.minmetals.com.cn",  
    "Origin": "https://ec.minmetals.com.cn",  
    "Pragma": "no-cache",  
    "Referer": "https://ec.minmetals.com.cn/open/home/purchase-info?tabIndex=0",  
    "Sec-Ch-Ua": "\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", \"Google Chrome\";v=\"122\"",  
    "Sec-Ch-Ua-Mobile": "?0",  
    "Sec-Ch-Ua-Platform": "\"Windows\"",  
    "Sec-Fetch-Dest": "empty",  
    "Sec-Fetch-Mode": "cors",  
    "Sec-Fetch-Site": "same-origin",  
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36"  
}  

ming_dic = {  
    "businessClassfication": "",  
    "dwmc": "",  
    "inviteMethod": "",  
    "lx": "ZBGG",  
    "mc": "",  
    "pageIndex": 1  
}  

pub_url = "https://ec.minmetals.com.cn/open/homepage/public"  

session = requests.session()  

session.headers = headers  

resp = session.post(url=pub_url)  

import execjs  

f = open("wukuang.js", mode="r", encoding="utf-8")  
js_code = f.read()  
f.close()  

js = execjs.compile(js_code)  

payload = js.call("fn", ming_dic, resp.text)  

content_url = "https://ec.minmetals.com.cn/open/homepage/zbs/by-lx-page"  

resp = session.post(url=content_url, data=payload)  

print(resp.json())