当前位置: 首页 > news >正文

爬虫逆向--Day25Day26--京东h5st案例解析

案例地址链接:https://search.jd.com/Search?keyword=%E7%99%BD%E9%85%92&enc=utf-8&pvid=53c39759079d4b92a3e6d3ea71974571

案例爬取链接:https://api.m.jd.com/api?appid=search-pc-java&t=1759021222491&client=pc&clientVersion=1.0.0&cthr=1&uuid=1759021192300961326017&loginType=3&keyword=%E7%99%BD%E9%85%92&functionId=pc_search_searchWare&body={%22enc%22:%22utf-8%22,%22pvid%22:%2253c39759079d4b92a3e6d3ea71974571%22,%22area%22:%221_72_55652_0%22,%22page%22:1,%22s%22:1}&x-api-eid-token=jdd03PNQPCXFNEW775N652WCJ7SFKNDCIVT3QCO7HIPIDZZQHITB6EHU2T7RJP4RAAXX474SZMRFKEOLKRYZDT5V4O4UONEAAAAMZRXKU7EIAAAAAC7FTSK4VTJR3OYX&h5st=20250928090024497;zm6g9g393pqjh336;f06cc;tk05w2f6134c941lMysxWXRhVG8yfZBUuVeH3caFu9uV68uIrt7DhhRhIrJsXabO_UOUYk9VCMaIW4sV8g_VqNeV8AeI;5079a9c20f5d8b943e2e27c6305df7d9;5.2;1759021222497;gt6f-JuVuB7HwgqVoVuIoF7U046ZB5_ZxI7ZBh-f1BeZnZ-G_U7ZBh-f1ZfIpR7V98OVuV7U8ULIpNLUAQOIrR_Utd7Vvd7V8c7IqZfZnZfFbwrI-MrE-hfZXx-Z9UuJwJuJ8M_J7ULIoRLTxdrUvJ_VvdbT_UuJwd_VqN_ZB5_Zuc7EzcrJ-hfZXx-ZxZfZnZfUsY7ZBh-f1ZfVzZ_WsJqK8wLH7kMU5YfZnZ-E-hfZXx-Z8YuHv98UwheV-h-T-trG9oLJvYfZB5hW-ZuVz8rM-h-T-JbF-hfZXxPCBh-f-J7Q-h-T-VOVsY7ZBhfZB5hWvh-T-dOVsY7ZBhfZB5hWtdeZnZfVwN6J-hfZBh-f1BOWB5_ZvdOE-YfZBhfZXx-ZNIqGLcbVuYOPPQaGuYfZnZPGyQ7GAY6ZBhfZB5hWxh-T-BOE-YfZBhfZXxfVB5_ZqN6J-hfZBh-f1d_VB5_ZrN6J-hfZBh-f1heZnZPUsY7ZBhfZB5hWxh-T-ROE-YfZBhfZXxvUth-T-VOE-YfZBhfZXx-ZrpPVzh_ZB5_ZwN6J-hfZBh-f1heZnZvHqYfZBhfZXxPUB5_Zuw7ZBhfZB5hWxh-T-x7ZBhfZB5hWxh-T-RrE-hfZBh-fmg-T-R7G8QaD8YfZB5hWkgfZXZPItR_JrJuJAMOV-YOUCQ7H-h-T-ZeF-hfZBh-fmg-T-haF-hfZXx-ZtJeDB1eUrpLHKgvTxpfVwhfMTgvFqkbIz8rM-h-T-dLEuYfZB5xD;81cf5c0c0b8d93f9a2363ab39520e008;gRaW989Gy8bE_oLE7w-Gy8rFvM7MtoLI4wrJ1R6G88bG_wPD9k7J1RLHxgKJ&t=1759021222505

一、案例【京东载荷h5st字段】

1.1、入口定位

image

 这次的目标很明确,我们就是需要破解京东的搜索接口的h5st字段,首先我们先通过关键字进行搜索,搜索h5st 

image

image

 首先我们先找到我们的目标url复制到【https://curlconverter.com/】中生成基础爬虫代码

image

 很明显,人家是做了加密处理,根本不给你返回任何的数据,所以我们只有破解h5st才可以获取到数据

1.2、代码分析

image

 点击进入到代码内部

image

 进来以后这里就两行代码,其中第二行【return _$tP.resolve(_$Ga);】就是包装,把这个带有h5st的值直接包装到promise中去,所以关键点还是第一行代码【var _$Ga = this._$sdnmd(_$Gf)】中的this._$sdnmd(_$Gf)就生成了h5st了

【这一步直接出结果就和promise没关系了,下一步就是包装promise】

image

 window.PSign.sign(d).then(res=>{console.log(res)})

这行代码会生成一个promise,所以我们执行上面的这行代码就会生成promise的结果

1.3、扣JS

image

 因为主要是this._$sdnmd(_$Gf); 这句代码生成的h5st所以把这句代码直接扣走

参数_$Gf是直接传递来的,所以我们拼接_$Gf,进行测试,缺什么补什么

image

由此可得d就是参数_$Gf,所以进行拼接即可

image

 在扣完代码拼接完paramsH5sign和params以后,我们就需要确定调用this._$sdnmd中的this 其实就是window.ParamsSign = _$Gk,

所以我们可以进行替换,

参数加密处理的逻辑就是,先对参数params进行SHA256处理,然后把处理完的结果放到paramsH5sign.body中,然后在对paramsH5sign参数进行处理生成h5st

所以我们替换完this以后,我们需要找到window.ParamsSign

image

 通过判断是new出来的,所以我们只需要扣try中的正常处理逻辑即可

image

 当把所有的代码都扣出来完成以后,允许就会报window找不到,就补window

这次又报document找不到,我们可以在补document,补完了又会报Element

这样其实我们第一阶段就已经完成了,下面我们就需要开始链式补环境

image

 当我们把window和document还有Element补完以后,结果就会出来了,就有h5st这个结果了,但是这个结果是错误的,在python中运行是得不到结果的,因为没过服务器的检测点,并且这个和h5st的长度也没关系,因为这个代码中他们做了原型链的检测,所以和长度什么的没关系,

所以下面我们还是需要分析原型链关系,根据原型链关系进行补环境

image

 看这个div标签的属性和方法,在属性和方法中找它的原型链关系

image

 通过查看可以得到Element继承Node ,Node继承EventTarget, EventTarget 继承Object

image

 如果提示报错,我们不知道如何处理的时候,我们就复制报错的地方,在源码中找到,断点定位到该处,进行判断是什么报错找不到,处理使用代码以外还需要会断点调试什么的,进行查错,

1.4、补环境【 补环境框架,V8引擎】

有的时候在源码中,他们会判断,Element是否有,并且有的时候也会判断Element的原型链它的父亲是否有,Element它的爷爷是否有,所以我们需要通过原型链去补环境,而不能直接简单的补一下

// 补环境
window = {}
window.document = {}
function Element(){}

有些网站它不检测原型链,我们按照上面的方式补环境是可以的,但是当有些网站检测原型链的时候,我们在按照上面的基础补环境就不行了

image

image

image

 以上是一段准备代码,方便我们通过链式补环境,

// 二、补环境
// (1) window对象处理
global_val = globalThis   // globalThis  其实就是global  就是node中的顶级变量
// 给global加一个window属性哈  给global全局配一个window属性   给global顶级变量挂载一个全局变量window
Object.defineProperty(global_val, "window", {get: function () {return global_val},set: function set_window(val) {global_val = val},enumerable: true,configurable: true,})
// 无论代码调取那个都是指向这个window  做一个全局处理
window.top = window.self = window.window = window

以上是对window缺少找不到进行的补环境处理

image

 小window --》 大Window --》 WindowProperties --》 EventTarget  --》 Object

小window其实是来自于大Window的一个实例化对象,小window的原型对象其实就是大Window

所以我们如果都补全了最好就是四层,目前我们该案例就写了两层

// 四层都不齐全
createConstructor("EventTarget", true, [], {})
createConstructor("WindowProperties", true, [], {},"EventTarget" )
createConstructor("Window", true, [], {},"WindowProperties" )
// 只补两层
createConstructor("Window", true, [], {})

image

window.document  报document找不到,因为document中的东西太多,所以我们先简单的补一下

window.document = {} 先跳过

image

我们先window.document = {} 跳过document ,先处理Element

Element 首字母大写的,我们都可以当作是类来处理,查看它的原型链

直接dir(Element),查看一下它爹,它爷这些原型链即可

image

/*
* Element --》 Node --》 EventTarget --》 Object
* 因为Element的父类是Node,Node的父类是EventTarget,EventTarget的父类Object,而且在createConstructor方法中,每次在最后面也只能添加一层的父类
* 所以我们给Element设置完父类Node以后,还需要给Node以同样的方式设置父类EventTarget  连续三个createConstructor,这才完成了一个原型串
* */
createConstructor("EventTarget", true, [], {})   // EventTarget 后面就没爹了,没爹不用写,默认是Object
createConstructor("Node", true, ["parentNode", "childNodes"], {}, "EventTarget")
createConstructor("Element", true, ["childElementCount", "innerHTML"], {}, "Node")

image

到这里,已经没有任何缺少环境的信息提示了,报一个看不懂对象的错误,所以这里我们就需要去添加监控了

image

 添加完监控以后,就会吐很多关于window相关的信息,但是这些window相关的信息都没什么用

所以我们还需要监听另外的一个,需要监控document。

image

 实例化出错了

 因为我们刚开始肯定是不知道,那个需要补,那个不需要补,但是现在假装我们已经知道了需要补那个环境,所以我们就挑重点的来补

image

 首先我们看到document调用了all

所以我们还是先dir(document.all)

image

image

// 补document的爹和爷  document --》 HTMLDocument  --》 Document --》 node
createConstructor("Document", true, [], {}, "Node")
createConstructor("HTMLDocument", true, [], {}, "Document")
// 这里是document实例化的时候传入的一些属性// all:new HTMLAllCollection(null,null,"ljc")  其实这样就实例化一个对象就可以了,但是还有一种可能是document.all.xxx 所以我们也需要在实例化的同时也监控一下// 所以我们就watch(new HTMLAllCollection(null,null,"ljc"),谁调用的呀)document.all.xxx,也就被监控了all:watch(new HTMLAllCollection(null,null,"ljc"),"document.all")

下一个就是dir(document.documentElement)

image

image

// 补document.documentElement  HTMLHtmlElement --》 HTMLElement  --》 Element --》 Node --》 EventTarget --》 Object
createConstructor("HTMLElement", true, [], {},"Element")
createConstructor("HTMLHtmlElement", true, [], {},"HTMLElement")
//按照上面的方式补全documentElementdocumentElement:watch(new HTMLHtmlElement(null,null,"ljc"),"document.documentElement")

下面在补一个cookie属性

image

 下一个dir(document.body)

image

image

// 补document.body   HTMLBodyElement -->  HTMLElement  -->  Element  -->  Node  --> EventTarget --> Object
createConstructor("HTMLBodyElement", true, [], {}, "HTMLElement")
//补bodybody: watch(new HTMLBodyElement(null, null, "ljc"), "document.body")

到目前为止,document的属性就都补完了,现在开始补方法了

image

 经过dir查看他们的链接关系,发现这些方法都在document这个类中,所以这些方法在那个类中,我们就应该补到那个类中

其中第一个优先补script

我们需要先查看他的链式关系

image

image

//补script
createConstructor("HTMLScriptElement", true, [], {}, "HTMLElement")
//补parentNode
createConstructor("HTMLHeadElement", true, [], {}, "HTMLElement")
createElement: function (tagName) {// 打印一下tagName 看一下创建的是那个标签console.log("Document prototype  createElement ", tagName)if (tagName === "script") {// 因为script后面也有可能调用了别的,所以我们在这里也需要进行监控script = watch(new HTMLScriptElement({parentNode: watch(new HTMLHeadElement(null, null, "ljc"), "script的parentNode")}, null, "ljc"), "script对象")return script}// canvas和上面的script就一样了if (tagName === "canvas") {return {}}},

image

 到这里,这个环境中已经有了script标签,虽然script调用这个parentNode找不到,但是至少不在报错了

所以我们这里就需要找这个parentNode是什么东西

image

 因为这个ele是咱们自己创建的,没有放到任何的一个父级标签中,所以ele.parent是null

image

image

因为这个srcipt在调用parent之前调用了什么,比如text/javascript,所以这个script标签都在head中,但是body中可能也有,所以我们就需要多在源码中搜索一下确定一下

所以我们在补这个parent的时候,这个script.parentNode就是那个head标签,因为script标签在head中,所以我们就需要确定这个head标签中的第0元素,然后确定它的创建类,来创建这个parentNode

image

image

 再往下就是补canvas  但是这个canvas可补,可不补,所以不需要关注

然后这个时候在运行,需要提前登录在运行,就会得到h5st 长度是556位,目前这个值是可以成功的

image

image

image

 把paramsH5sign作为参数传递进去,然后通过开通的接口就会生成h5st,所以以后在想拿h5st就不需要在和Python打交道了直接做成一个接口,之前用Python拿数据都是使用import execjs或者import subprocess 之前都是用Python调度js代码,如果有了定时器什么的就比较麻烦,所以现在就直接把JS生成接口,如果Python要用,就直接自己传参自己拿结果,访问一次获取一个h5st,每次获取的h5st还都不一样,因为是时间作为了参数获取的h5st

1.5、代码文件

该案例需要导入:pip install curl_cffi           npm install express

1.5.1、Python文件

import hashlib
from curl_cffi import requests
import time
import osdef sha256_hash(data: str) -> str:"""计算字符串的 SHA-256 哈希值:param data: 输入字符串:return: 十六进制表示的哈希值字符串"""hash_obj = hashlib.sha256(data.encode('utf-8'))return hash_obj.hexdigest()cookies = {'__jdv': '143920055|direct|-|none|-|1759198339989','__jdu': '1759198339988240393195','3AB9D23F7A4B3CSS': 'jdd0336CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4EAAAAMZTBSF5TAAAAAADED2AUUGHUOS44X','mba_muid': '1759198339988240393195','wlfstk_smdl': 'di09prnlss3k3bnvofmpwmkjo812881l','TrackID': '1RVWFVwuwegh6iaq2dUaSUyxtlv2W8ZsWmxP21tBeuzA6JLP01W25RW4ZNKGWhDFKBR_xMLfaTohtTorBRBpdNlxBRXHQ87UD-YA5CjBNN54','thor': '2DD9CF08CB217E996764A091B245EB29B43DEECE308DCF74BCBC66126340B7CC57EDD320176A72EA74043C4CDD7F4324D872C1E3813F23CFC94796AE1ABFB72BC0EB76990CBC9F42DD82C52859CD62E4DF2374343ED9606AFB4437FA8D8FACB1753AD87ACE74EDE1B970E782C73FFEFB25EBC28E00E7D303C43E5B5FDC1C3826210EFE4E1F9CC0F684515F5703EB90D7302414E086525715F186A95323BEAE81','light_key': 'AASBKE7rOxgWQziEhC_QY6yaKYl0-nkwHhCq7N-FMTwrP69jFjUruev7Rsm2eNMxzUBY6VIP','pinId': 'w8Ih7LOJnxwxhZalSpUGsbV9-x-f3wj7','pin': 'jd_52b15ac66b44e','unick': '%E5%91%86%E5%91%867115','ceshi3.com': '203','_tp': 'oRxO98xexSXw5ZlTXC2fbDrx5URIy94AUL5RT79KEbs%3D','_pst': 'jd_52b15ac66b44e','__jdc': '143920055','areaId': '5','shshshfpa': 'b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380','shshshfpx': 'b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380','ipLoc-djd': '1-72-55653-0','shshshfpb': 'BApXSoJBtm_xAUGY1ek_5MROtpLvWQ2uQBhtYhK9o9xJ1MhEHxo62','3AB9D23F7A4B3C9B': '36CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4E','flash': '3_6gpgATZhoG6k8czgYn2HnA7xPO3mh2cXAmEzCQ0z422kecKRpx89Biu_-2dtCY3V_jARZ4wIxT3UKzcCONXY1gr97YLyPbCHYjhWNhvMGQGBq_IB6Udnm-kw105qUMUkmMKnu5lY-1pCuNNxyfD0jxFU2_-xm1QimzdddFQTJfmsemqUvJBa3V**','__jda': '143920055.1759198339988240393195.1759198340.1759198340.1759200581.2','__jdb': '143920055.1.1759198339988240393195|2.1759200581','sdtoken': 'AAbEsBpEIOVjqTAKCQtvQu179H4TgU1sQiwY_jgIlLkfzub2EneuGP2sLpz488hVNgAiBKppDnR-9KGdRFMb2fGttHfYI6x6ZfMbttuK65GA90pBEalHNm_-LbXYXth3my7jHZ5D0UqmCIA',
}headers = {'accept': 'application/json, text/plain, */*','accept-language': 'zh-CN,zh;q=0.9','origin': 'https://search.jd.com','priority': 'u=1, i','referer': 'https://search.jd.com/','sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"Windows"','sec-fetch-dest': 'empty','sec-fetch-mode': 'cors','sec-fetch-site': 'same-site','user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36','x-referer-page': 'https://search.jd.com/Search','x-rp-client': 'h5_1.0.0',# 'cookie': '__jdv=143920055|direct|-|none|-|1759198339989; __jdu=1759198339988240393195; 3AB9D23F7A4B3CSS=jdd0336CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4EAAAAMZTBSF5TAAAAAADED2AUUGHUOS44X; mba_muid=1759198339988240393195; wlfstk_smdl=di09prnlss3k3bnvofmpwmkjo812881l; TrackID=1RVWFVwuwegh6iaq2dUaSUyxtlv2W8ZsWmxP21tBeuzA6JLP01W25RW4ZNKGWhDFKBR_xMLfaTohtTorBRBpdNlxBRXHQ87UD-YA5CjBNN54; thor=2DD9CF08CB217E996764A091B245EB29B43DEECE308DCF74BCBC66126340B7CC57EDD320176A72EA74043C4CDD7F4324D872C1E3813F23CFC94796AE1ABFB72BC0EB76990CBC9F42DD82C52859CD62E4DF2374343ED9606AFB4437FA8D8FACB1753AD87ACE74EDE1B970E782C73FFEFB25EBC28E00E7D303C43E5B5FDC1C3826210EFE4E1F9CC0F684515F5703EB90D7302414E086525715F186A95323BEAE81; light_key=AASBKE7rOxgWQziEhC_QY6yaKYl0-nkwHhCq7N-FMTwrP69jFjUruev7Rsm2eNMxzUBY6VIP; pinId=w8Ih7LOJnxwxhZalSpUGsbV9-x-f3wj7; pin=jd_52b15ac66b44e; unick=%E5%91%86%E5%91%867115; ceshi3.com=203; _tp=oRxO98xexSXw5ZlTXC2fbDrx5URIy94AUL5RT79KEbs%3D; _pst=jd_52b15ac66b44e; __jdc=143920055; areaId=5; shshshfpa=b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380; shshshfpx=b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380; ipLoc-djd=1-72-55653-0; shshshfpb=BApXSoJBtm_xAUGY1ek_5MROtpLvWQ2uQBhtYhK9o9xJ1MhEHxo62; 3AB9D23F7A4B3C9B=36CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4E; flash=3_6gpgATZhoG6k8czgYn2HnA7xPO3mh2cXAmEzCQ0z422kecKRpx89Biu_-2dtCY3V_jARZ4wIxT3UKzcCONXY1gr97YLyPbCHYjhWNhvMGQGBq_IB6Udnm-kw105qUMUkmMKnu5lY-1pCuNNxyfD0jxFU2_-xm1QimzdddFQTJfmsemqUvJBa3V**; __jda=143920055.1759198339988240393195.1759198340.1759198340.1759200581.2; __jdb=143920055.1.1759198339988240393195|2.1759200581; sdtoken=AAbEsBpEIOVjqTAKCQtvQu179H4TgU1sQiwY_jgIlLkfzub2EneuGP2sLpz488hVNgAiBKppDnR-9KGdRFMb2fGttHfYI6x6ZfMbttuK65GA90pBEalHNm_-LbXYXth3my7jHZ5D0UqmCIA',
}# 访问的接口
url = "https://api.m.jd.com/api"# body数据 就是一个json数据 有第几页   在这里修改page
body = "\\{\"page\":1,\"area\":\"7_527_35108_56983\",\"pvid\":\"c7edfed410bb4172956eb62ce0ad72f4\"\\}"
# 去我们通过express开放的接口去获取h5st
response = requests.post("localhost:3000/api/data", json={"params": {"appid": "search-pc-java","functionId": "pc_search_adv_Search","body": sha256_hash(body)   # 把这个body做了一下sha256}
})param = response.json()["result"]
print("param:::",len(param['h5st']))params = {"appid": "search-pc-java","t": [str(param["t"]),str(int(time.time() * 1000))],"client": "pc","clientVersion": "1.0.0","uuid": "1745841960334352365307","keyword": "手机",   # 在这里修改类型"functionId": "pc_search_getShopAndWare","body": body,"x-api-eid-token": "jdd03QJJ7DOUYP7T5O2IKSRFQANXZJYHALCU3ECRYXYVSULUXN7DODVWDAGUVYK2WLOTISQ3XQ7U7G5PP57CC2QFRLQFA5AAAAAMYYUR7TVIAAAAACORWVTOFTVSAUQX","h5st": param["h5st"]
}
response = requests.get(url, headers=headers, cookies=cookies, params=params, impersonate='chrome101')print(response.text)
print(response)

1.5.2、express文件

// 引入 express
const express = require('express');
const app = express();
const fs = require('fs');
const { get_h5st } = require('./04 h5st');
ljc_log = console.log;// 支持 JSON 请求体解析
app.use(express.json());// 一个 GET 接口
app.get('/api/hello', (req, res) => {res.json({ message: 'Hello from Node.js backend!' });
});// 一个 POST 接口
app.post('/api/data', (req, res) => {// eval(fs.readFileSync('myserver\\jingdong\\5.2.0\\env.js', 'utf8'))ljc_log('接收到数据:', req.body["params"]);res.json({ status: 'success', result: get_h5st(req.body["params"]) });
});// 监听端口
const PORT = 3000;
app.listen(PORT, () => {console.log(`✅ Server is running at http://localhost:${PORT}`);
});/*
* express 就是编程里面的web框架  就是对外开一个接口
* npm install express  需要安装一下这个
* */

1.5.3、JS文件

image

 以上是:一、补环境2个工具 在任何的案例中都可以拿过去用

image

 以上是:三、源码

image

cryptoJs = require("crypto-js")// 一、补环境2个工具  在任何的案例中都可以拿过去用
// (1)监控函数
// ***************************第一个补环境工具**************************
// (2)函数构造器
/*** 创建具有特定属性和方法的构造函数* 该函数主要用于浏览器环境模拟,可以创建具有严格访问控制、继承支持和原型方法的构造函数* 通过外部数据存储和Symbol键实现实例数据隔离,提高安全性和防检测能力** @param {string} constructorName - 构造函数的名称,将作为全局变量挂载到window对象上* @param {boolean} enableStrictMode - 是否启用严格模式验证,启用后需要特定令牌才能调用构造函数* @param {Array} [propertiesList=[]] - 属性定义列表,支持简单字符串属性或自定义属性描述符*        - 简单属性:字符串形式,如 "name"*        - 自定义属性:数组形式,如 ["all", {get: function() {...}}]* @param {Object} [prototypeMethods={}] - 要添加到原型上的方法* @param {string} [parentConstructorName=null] - 父构造函数的名称,用于实现继承* @returns {Function} 返回新创建的构造函数,同时会挂载到window对象上** @example* // 创建简单的Person构造函数* createConstructor("Person", true, ["name", "age"], {*   greet: function() { return `Hello, ${this.name}`; }* });** function Person(){*     this.name = name*     this.age = age*     greed:function(){*       return `Hello, ${this.name}`;*     }* }*** @example* // 创建继承自Animal的Dog构造函数* createConstructor("Dog", true, ["breed"], {*   bark: function() { return "Woof!"; }* }, "Animal");** function Dog(){    这个Dog继承Animal*     this.name = name*     this.age = age*     bark: function() {*          return "Woof!";*     }* }*/
function createConstructor(constructorName, enableStrictMode, propertiesList = [], prototypeMethods = {}, parentConstructorName = null) {// 使用对象存储所有实例的数据,实现数据隔离// 每个实例通过Symbol键关联到其数据,提高安全性const instancesData = {};// 创建构造函数const Constructor = function (element, propertySetter, validationToken) {// 验证构造函数调用合法性if (enableStrictMode && !(validationToken && validationToken === "ljc")) {throw new Error("Illegal constructor");}// 调用父构造函数(如果存在)if (parentConstructorName && window[parentConstructorName]) {window[parentConstructorName].call(this, element, null, "ljc");}// 设置对象属性if (propertySetter && typeof propertySetter === "function") {propertySetter(this);}// 初始化元素属性const instanceProperties = element && typeof element === "object" ? {...element} : {};// 创建唯一标识符this._element = Symbol('_element');instancesData[this._element] = instanceProperties;};// 设置继承关系if (parentConstructorName && window[parentConstructorName]) {Constructor.prototype = Object.create(window[parentConstructorName].prototype);Constructor.prototype.constructor = Constructor;Constructor.__proto__ = window[parentConstructorName];}// 设置toStringTagObject.defineProperty(Constructor.prototype, Symbol.toStringTag, {value: constructorName,writable: false,enumerable: false,configurable: true});// 添加原型方法Object.keys(prototypeMethods).forEach(methodName => {Constructor.prototype[methodName] = prototypeMethods[methodName];});// 将构造函数挂载到全局对象window[constructorName] = Constructor;return Constructor;
}// 二、补环境
// (1) window对象处理
global_val = globalThis   // globalThis  其实就是global  就是node中的顶级变量
// 给global加一个window属性哈  给global全局配一个window属性   给global顶级变量挂载一个全局变量window
Object.defineProperty(global_val, "window", {get: function () {return global_val},set: function set_window(val) {global_val = val},enumerable: true,configurable: true,})
// 无论代码调取那个都是指向这个window  做一个全局处理
window.top = window.self = window.window = window// (2)补原型链环境
/* 补大Window
* 小window和大Window是两个东西,小window类似于是大Window的实例化对象,大Window是类的概念
* */
// 创建Window空间   createConstructor 一般处理的都是大Window这样的类对象,小window一半都是我们自己构建
createConstructor("Window", true, [], {})
console.log(Window)
// 小window需要和大Window建立父子关系
window.__proto__ = Window.prototype
// 给小window补toStringTag方法  因为小window是单独处理的,所以需要单独把这些加进来
Object.defineProperty(window, Symbol.toStringTag, {value: "Window",   // '[object Window]'  因为是这个字符串所以需要返回Windowwritable: false,enumerable: false,configurable: true
});window.document = {}/* 补Element
* Element --》 Node --》 EventTarget --》 Object
* 因为Element的父类是Node,Node的父类是EventTarget,EventTarget的父类Object,而且在createConstructor方法中,每次在最后面也只能添加一层的父类
* 所以我们给Element设置完父类Node以后,还需要给Node以同样的方式设置父类EventTarget  连续三个createConstructor,这才完成了一个原型串
* */
createConstructor("EventTarget", true, [], {})   // EventTarget 后面就没爹了,没爹不用写,默认是Object
createConstructor("Node", true, ["parentNode", "childNodes"], {}, "EventTarget")
createConstructor("Element", true, ["childElementCount", "innerHTML"], {}, "Node")// 补document的爹和爷  document --》 HTMLDocument  --》 Document --》 node  经过查看链,这些方法都在Document中,所以就补在Document类中
createConstructor("Document", true, [], {createElement: function (tagName) {// 打印一下tagName 看一下创建的是那个标签console.log("Document prototype  createElement ", tagName)if (tagName === "script") {// 因为script后面也有可能调用了别的,所以我们在这里也需要进行监控script = watch(new HTMLScriptElement({parentNode: watch(new HTMLHeadElement(null, null, "ljc"), "script的parentNode")}, null, "ljc"), "script对象")return script}// canvas和上面的script就一样了if (tagName === "canvas") {return {}}},createEvent: function () {},querySelector: function () {},getElementsByTagName: function () {},
}, "Node")
createConstructor("HTMLDocument", true, [], {}, "Document")// 补document.all 的爹直接就是Object
createConstructor("HTMLAllCollection", true, [], {})// 补document.documentElement  HTMLHtmlElement --》 HTMLElement  --》 Element --》 Node --》 EventTarget --》 Object
createConstructor("HTMLElement", true, [], {}, "Element")
createConstructor("HTMLHtmlElement", true, [], {}, "HTMLElement")// 补document.body   HTMLBodyElement -->  HTMLElement  -->  Element  -->  Node  --> EventTarget --> Object
createConstructor("HTMLBodyElement", true, [], {}, "HTMLElement")//补script
createConstructor("HTMLScriptElement", true, [], {}, "HTMLElement")
//补parentNode
createConstructor("HTMLHeadElement", true, [], {}, "HTMLElement")// (3)添加监控
// 监控document
// window.document = watch({},"document")   给document对象先置空{},然后在监控起来,监控的名字是"document",在返回window.document
//如果案例不检测原型链,我们就可以使用基础补环境直接documenti = {}  按照上面的先置空,然后在监控,但是本案例对原型链进行了检测,
// 所以我们需要先通过dir(document)先找到它爹它爷,然后补HTMLDocument --》Document --》 node这样的原型链出来, 最后再用创建的爹HTMLDocument new一个document对象出来
window.document = watch(new HTMLDocument({// 这里是document实例化的时候传入的一些属性// all:new HTMLAllCollection(null,null,"ljc")  其实这样就实例化一个对象就可以了,但是还有一种可能是document.all.xxx 所以我们也需要在实例化的同时也监控一下// 所以我们就watch(new HTMLAllCollection(null,null,"ljc"),谁调用的呀)document.all.xxx,也就被监控了all: watch(new HTMLAllCollection(null, null, "ljc"), "document.all"),//按照上面的方式补全documentElementdocumentElement: watch(new HTMLHtmlElement(null, null, "ljc"), "document.documentElement"),//在补一个cookie属性cookie: '__jdv=143920055|direct|-|none|-|1759198339989; __jdu=1759198339988240393195; 3AB9D23F7A4B3CSS=jdd0336CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4EAAAAMZTBSF5TAAAAAADED2AUUGHUOS44X; _gia_d=1; xapieid=jdd0336CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4EAAAAMZTBSF5TAAAAAADED2AUUGHUOS44X; mba_muid=1759198339988240393195; mba_sid=17591983546191142823231.1; wlfstk_smdl=di09prnlss3k3bnvofmpwmkjo812881l; TrackID=1RVWFVwuwegh6iaq2dUaSUyxtlv2W8ZsWmxP21tBeuzA6JLP01W25RW4ZNKGWhDFKBR_xMLfaTohtTorBRBpdNlxBRXHQ87UD-YA5CjBNN54; pinId=w8Ih7LOJnxwxhZalSpUGsbV9-x-f3wj7; pin=jd_52b15ac66b44e; unick=%E5%91%86%E5%91%867115; ceshi3.com=203; _tp=oRxO98xexSXw5ZlTXC2fbDrx5URIy94AUL5RT79KEbs%3D; __jda=143920055.1759198339988240393195.1759198340.1759198340.1759198340.1; __jdb=143920055.4.1759198339988240393195|1.1759198340; __jdc=143920055; o2State=; is_avif=onAVIF; areaId=5; shshshfpa=b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380; shshshfpx=b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380; cn=160; ipLoc-djd=1-72-55653-0; shshshfpb=BApXSoJBtm_xAUGY1ek_5MROtpLvWQ2uQBhtYhK9o9xJ1MhEHxo62; sdtoken=AAbEsBpEIOVjqTAKCQtvQu17mfvlXCqDS0kDr1URwxQHljkUa_9A_ZlGxFL5FumtPOEc2qL3IJkeuNb1O6o04CUR4DMk0etLSPh823yKNu4WG_MO2ZAQTa1qerWUv7eZjfgraGRgUhS_8NI; 3AB9D23F7A4B3C9B=36CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4E',//补bodybody: watch(new HTMLBodyElement(null, null, "ljc"), "document.body"),}, null, "ljc"), "document")
// 监控window,名字是"window" 重新用window   重新用的名字 = watch(监控对象, 监控的名字)
window = watch(window, "window")// 三、源码
// ***************************此处补充源码**************************// 四、测试
// 使用源码进行new,通过实例化创建window.PSign对象
/*window.PSign = new window.ParamsSign({appId: 'f06cc',preRequest: false,onSign: (res) => {// 签名可用率监控,业务方自行上报if (res.code != 0) {try {window.dra &&window.dra.sendCustomEvent &&window.dra.sendCustomEvent({name: 'main_search',metrics: {error_code: '751',error_type_txt: '接口加密失败onSign非0',},context: {error_code: res.code,},})} catch (error) {console.log(error)}}},onRequestTokenRemotely: (res) => {// 算法接口可用率监控,业务方自行上报if (res.code != 200) {try {window.dra &&window.dra.sendCustomEvent &&window.dra.sendCustomEvent({name: 'main_search',metrics: {error_code: '751',error_type_txt: '接口加密失败onRequestTokenRemotely',},context: {error_msg: res && res.message ? res.message : '接口加密失败',},})} catch (error) {console.log(error)}}},
})const paramsH5sign = {appid: 'search-pc-java',functionId: "pc_search_adv_Search",client: 'pc',clientVersion: '1.0.0',t: new Date().getTime(),
}
params = {"ad_ids": "292:6","xtest": "new_search","ec": "utf-8","area": "1","page": "2","simpleSearch": "0"
}
paramsH5sign.body = cryptoJs.SHA256(JSON.stringify(params))// 使用实例化创建的对象,替换this
ret = window.PSign._$sdnmd(paramsH5sign);
console.log(":::::", ret)*/function main(paramsH5sign) {//取源码// ***************************此处补充源码**************************// new 实例化encrypt = new ParamsSignMain({appId: "fb5df",debug: false,onSign: function (t) {},onRequestToken: function (t) {},onRequestTokenRemotely: function (t) {},bucket: "0.1.8"})// 传进来的 paramsH5sign 只在这里进行了取值操作result = encrypt._$sdnmd({"appid": paramsH5sign['appid'],//"item-v3","functionId": paramsH5sign['functionId'],//"pc_stocks","client": "pc","clientVersion": "1.0.0","t": +new Date(),"body": paramsH5sign['body']})console.log("H5ST:::", result["h5st"].length, result)document_all = 0return result
}//  module.exports js的导出,导出那个函数,导出main这个函数,对外叫什么名字,对外叫get_h5st
module.exports = {get_h5st: main}

http://www.dtcms.com/a/427759.html

相关文章:

  • 深圳拼团网站建设网页游戏网游
  • 特征值与特征向量与SVD矩阵分解
  • 建设网站站点有哪些步骤网站建设 搜狐
  • Java Deque 和 ArrayDeque(栈的推荐使用) 基本介绍以及使用
  • 网站建设提供资料没有后台的网站怎么做排名
  • 使用BatchNorm偏置填充边界:确保推理一致性与数值稳定性
  • 山东官方网站建设网站配置优化
  • 14. 初识 SPDK
  • C++ STL insert和emplace函数区别
  • 湖北省建设厅行政审批网站哈尔滨优化调整人员流动管理
  • Windows 工作组环境信息收集与系统配置
  • QML学习笔记(二十一)QML的跨组件发送信号
  • 福田网站建设费用做企业网站的意义
  • 安卓人机验证View
  • Android AIDL通信案例
  • 活动报名开启|GitCode X deepin Meetup 深圳站
  • 俄罗斯网站制作电商网站开发数据库设计
  • 网站建设征求意见表wordpress 环境
  • 论软件的系统测试及其应用(2016年上半年)
  • 不同坐标转换方式的误差分析:免费方法能替代高德官方API吗?
  • 自助seo网站建设杭州系统开发
  • 美食介绍网站模板深圳门户网站建设特点
  • 那些网站可以做海报湖南常德广宇建设网站
  • 特殊环境下的丝杆支撑座选型要点
  • 乳品防伪溯源:为行业安全赋能,构建全链路坚实屏障
  • 企业微信-智能表格创建
  • 建设工程安全员教育网站网站怎么优化搜索
  • 【办公类-115-02】20250920职称资料上传02——多个jpg合并一个PDF(如:教师资格证、聘任表)和压缩PDF的大小(控制在5MB以内)
  • wordpress多站点 主题网站开发的安全问题
  • 极简主义伴侣-数学模块MVP具体技术实现的规划