爬虫prc技术----小红书爬取解决xs

devtools/2024/10/9 1:19:20/

知识星球:知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具知识星球是创作者连接铁杆粉丝,实现知识变现的工具。任何从事创作或艺术的人,例如艺术家、工匠、教师、学术研究、科普等,只要能获得一千位铁杆粉丝,就足够生计无忧,自由创作。社群管理、内容沉淀、链接粉丝等就在知识星球。icon-default.png?t=O83Ahttps://articles.zsxq.com/id_5x1m9wdv3e20.html

目录

知识星球:知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具知识星球是创作者连接铁杆粉丝,实现知识变现的工具。任何从事创作或艺术的人,例如艺术家、工匠、教师、学术研究、科普等,只要能获得一千位铁杆粉丝,就足够生计无忧,自由创作。社群管理、内容沉淀、链接粉丝等就在知识星球。https://articles.zsxq.com/id_5x1m9wdv3e20.html

一,什么是websocket

二,websocket的原理

三,websocket与http的关系

四,websocket解决的问题

1.http存在的问题

2.long poll(长轮询)

3.Ajax轮询

4.websocket的改进

五 websocket实现方式

1. 客户端

2.服务端

六。案例

效果

具体代码:

RPC

1. RPC 简介

2.Sekiro-RPC

1. 使用方法

2. 测试使用

3.案例


一,什么是websocket

WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)

它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的

Websocket是一个持久化的协议

二,websocket的原理

websocket约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp的连接,从而方便它们之间的通信

在websocket出现之前,web交互一般是基于http协议的短连接或者长连接

websocket是一种全新的协议,不属于http无状态协议,协议名为"ws"

三,websocket与http的关系

相同点:

都是基于tcp的,都是可靠性传输协议

都是应用层协议

不同点:

WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息

HTTP是单向的

WebSocket是需要浏览器和服务器握手进行建立连接的

而http是浏览器发起向服务器的连接,服务器预先并不知道这个连接

联系:

WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的

总结(总体过程):

首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;

然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;

最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信。

四,websocket解决的问题

1.http存在的问题

http是一种无状态协议,每当一次会话完成后,服务端都不知道下一次的客户端是谁,需要每次知道对方是谁,才进行相应的响应,因此本身对于实时通讯就是一种极大的障碍

http协议采用一次请求,一次响应,每次请求和响应就携带有大量的header头,对于实时通讯来说,解析请求头也是需要一定的时间,因此,效率也更低下

最重要的是,需要客户端主动发,服务端被动发,也就是一次请求,一次响应,不能实现主动发送

2.long poll(长轮询)

对于以上情况就出现了http解决的第一个方法——长轮询

基于http的特性,简单点说,就是客户端发起长轮询,如果服务端的数据没有发生变更,会 hold 住请求,直到服务端的数据发生变化,或者等待一定时间超时才会返回。返回后,客户端又会立即再次发起下一次长轮询

优点是解决了http不能实时更新的弊端,因为这个时间很短,发起请求即处理请求返回响应,实现了“伪·长连接”

张三取快递的例子,张三今天一定要取到快递,他就一直站在快递点,等待快递一到,立马取走

从例子上来看有个问题:

假如有好多人一起在快递站等快递,那么这个地方是否足够大,(抽象解释:需要有很高的并发,同时有很多请求等待在这里)

总的来看:

推送延迟。服务端数据发生变更后,长轮询结束,立刻返回响应给客户端。

服务端压力。长轮询的间隔期一般很长,例如 30s、60s,并且服务端 hold 住连接不会消耗太多服务端资源。

3.Ajax轮询

基于http的特性,简单点说,就是规定每隔一段时间就由客户端发起一次请求,查询有没有新消息,如果有,就返回,如果没有等待相同的时间间隔再次询问

优点是解决了http不能实时更新的弊端,因为这个时间很短,发起请求即处理请求返回响应,把这个过程放大n倍,本质上还是request = response

举个形象的例子(假设张三今天有个快递快到了,但是张三忍耐不住,就每隔十分钟给快递员或者快递站打电话,询问快递到了没,每次快递员就说还没到,等到下午张三的快递到了,but,快递员不知道哪个电话是张三的,(可不是只有张三打电话,还有李四,王五),所以只能等张三打电话,才能通知他,你的快递到了)

从例子上来看有两个问题:

假如说,张三打电话的时间间隔为10分钟,当他收到快递前最后一次打电话,快递员说没到,他刚挂掉电话,快递入库了(就是到了),那么等下一次时间到了,张三打电话知道快递到了,那么这样的通讯算不算实时通讯?很显然,不算,中间有十分钟的时间差,还不算给快递员打电话的等待时间(抽象的解释:每次request的请求时间间隔等同于十分钟,请求解析相当于等待)

假如说张三所在的小区每天要收很多快递,每个人都采取主动给快递员打电话的方式,那么快递员需要以多快的速度接到,其他人打电话占线也是问题(抽象解释:请求过多,服务端响应也会变慢)

总的来看,Ajax轮询存在的问题:

推送延迟。

服务端压力。配置一般不会发生变化,频繁的轮询会给服务端造成很大的压力。

推送延迟和服务端压力无法中和。降低轮询的间隔,延迟降低,压力增加;增加轮询的间隔,压力降低,延迟增高

4.websocket的改进

一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端断开WebSocket连接或Server端中断连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实现了“真·长链接”,实时性优势明显。

五 websocket实现方式

1. 客户端

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<input id="box" type="text">
<button onclick="ps()">发送</button><script>// 与服务器约定的连接 以及端口  本机的   hosts文件  localhost      www.ps.comconst websocket = new WebSocket('ws://127.0.0.1:8080/')//连接发生错误的回调方法websocket.onerror = () => {console.log("WebSocket连接发生错误");};//连接成功建立的回调方法websocket.onopen = function () {console.log("WebSocket连接成功");}//接收到消息的回调方法  接收服务器的数据websocket.onmessage = function (event) {console.log(event.data);}//连接关闭的回调方法websocket.onclose = function () {console.log("WebSocket连接关闭");}function ps() {// jquery -> val   JS -> valuevar text = document.getElementById('box').value// 客户端发信息发服务器websocket.send(text)}</script>
</body>
</html>

2.服务端

# encoding: utf-8
import asyncio
import websocketsasync def echo(websocket):# 使用WebSocket在客户端和服务器之间建立全双工双向连接后,就可以在连接打开时调用send()方法。message = 'hello world'# 发送数据await websocket.send(message)return Trueasync def recv_msg(websocket):while 1:# 接收数据recv_text = await websocket.recv()print(recv_text)async def main_logic(websocket, path):await echo(websocket)await recv_msg(websocket)start_server = websockets.serve(main_logic, '127.0.0.1', 8080)
loop = asyncio.get_event_loop()
loop.run_until_complete(start_server)
# 创建了一个连接对象之后,需要不断监听返回的数据,则调用 run_forever 方法,要保持长连接即可
loop.run_forever()

六。案例

基于此我们可以本地替换实时获取数据

案例网站:https://www.xiaohongshu.com

我们利用爬虫工具库-spidertools.cn快速构建请求

import requests
import jsonheaders = {"accept": "application/json, text/plain, */*","accept-language": "zh-CN,zh;q=0.9","cache-control": "no-cache","content-type": "application/json;charset=UTF-8","origin": "https://www.xiaohongshu.com","pragma": "no-cache","priority": "u=1, i","referer": "https://www.xiaohongshu.com/","sec-ch-ua": "\"Google Chrome\";v=\"129\", \"Not=A?Brand\";v=\"8\", \"Chromium\";v=\"129\"","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/129.0.0.0 Safari/537.36","x-b3-traceid": "4a3e0e2edcd69a5a","x-s": "XYW_eyJzaWduU3ZuIjoiNTUiLCJzaWduVHlwZSI6IngyIiwiYXBwSWQiOiJ4aHMtcGMtd2ViIiwic2lnblZlcnNpb24iOiIxIiwicGF5bG9hZCI6ImU5ZTczMzdlOTZiNGRmMTVkYTJjNDAyNWM3ZjIzNzRmZGQxMjc1ZmE5YWEyNWVjYzJiMmVjN2MxYjBmYmRlYjFkMzc1YmJjM2Y2MzZmZGQ5MTAxYmI5NDc0NDE0OTQzOTI5YmEwYTQwOGUxYmVmZTQ4NTA2ZGNkZGY0OWFhMzE4ZTY5Mzc3NjU0ODdhOWVlMTFhYTgwNWM0NDg5NDNkNjcwMmI2OTk4NmE2OTk1Nzc1ZTM4ZTkzMTdjN2EyNWMxMjNiYzM0Y2Y4OTVkMTA0NmE3YTAyZGQzZGIwMWUwZmI0ODdmZTU4NjZhNmQ2ZDBlMjk5YTUwOWZlNWE4ZDMyZjU3ZTM1ZmFmYTY0YjY1MDViYzBmYTBmYmU5YjlkZjVlM2ExNmQ0NmM0NTkwMDY3ZDI3YjNlZGZlZDI5MDg0NGE4OWUxYTIxODc1ZjRlN2I1OTc5MTRhYmE1MjVlNGVmMThjYzBiODkyZjA3ZmEwNTg2NjIxOGYyZmQzZThhOWY2OTcyYWQ3NjcyMmQwMjM1ZTQ1MjE2MWFlZDBkZWUxODQ1In0=","x-s-common": "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","x-t": "1727942080882","x-xray-traceid": "c928afdcb6b114122d4b8c7d18662d5e"
}
cookies = {}
url = "https://edith.xiaohongshu.com/api/sns/web/v1/homefeed"
data = {"cursor_score": "","num": 47,"refresh_type": 1,"note_index": 34,"unread_begin_note_id": "","unread_end_note_id": "","unread_note_count": 0,"category": "homefeed.food_v3","search_key": "","need_num": 22,"image_formats": ["jpg","webp","avif"],"need_filter_image": False
}
data = json.dumps(data, separators=(',', ':'))
response = requests.post(url, headers=headers, cookies=cookies, data=data)print(response.text)
print(response)

据包是homefeed

其中有X-s和X-s-common进行了加密

我们查找X-s赋值位置即可,全局搜索

其中x-s和x-t是由window._webmsxyw(c, i)生成的,其中c为'/api/sns/web/v1/homefeed',t为查询参数

比如

data = {"cursor_score": "","num": 39,"refresh_type": 1,"note_index": 34,"unread_begin_note_id": "","unread_end_note_id": "","unread_note_count": 0,"category": "binggaokao_feed_recommend","search_key": "","need_num": 14,"image_formats": ["jpg","webp","avif"],"need_filter_image": false
}

那么我们直接利用websocket借用浏览器的环境处理,本地替换

效果

具体代码:

本地替换

 !(function () {if (window.flag) {} else {const websocket = new WebSocket('ws://127.0.0.1:8080')// 创建一个标记用来判断是否创建套接字window.flag = true;//连接发生错误的回调方法websocket.onerror = () => {console.log("WebSocket连接发生错误");};//连接成功建立的回调方法websocket.onopen = function () {console.log("WebSocket连接成功");}//接收到消息的回调方法  接收服务器的数据websocket.onmessage = function (event) {console.log(event.data);}//连接关闭的回调方法websocket.onclose = function () {console.log("WebSocket连接关闭");}//  接收服务端发送的信息websocket.onmessage = function (event) {
。。。。。。。。。。。。。。。。。具体见知识星球}}}())

服务端

。。。。。。。。。。。。。。。。。具体见知识星球

利用rpc获取数据

RPC

1. RPC 简介

为什么要使用RPC技术呢?我们在使用websocket时候可以发现,python在操作的时候,需要创建连接,还需要不断去接受传递数据,非常的麻烦, 那这个时候rpc技术可以帮助到我们,简单来说就是网页直接和rpc服务器进行交互,我们python可以直接调用,rpc暴露的接口,不需要关心,创建连接这一块的问题.

RPC 技术是非常复杂的,对于我们搞爬虫、逆向的来说,不需要完全了解,只需要知道这项技术如何在逆向中应用就行了。

RPC 在逆向中,简单来说就是将本地和浏览器,看做是服务端和客户端,二者之间通过 WebSocket 协议进行 RPC 通信,在浏览器中将加密函数暴露出来,在本地直接调用浏览器中对应的加密函数,从而得到加密结果,不必去在意函数具体的执行逻辑,也省去了扣代码、补环境等操作,可以省去大量的逆向调试时间。

2.Sekiro-RPC

  • 官网地址:私有API导出框架 | sekiro
1. 使用方法

1. 执行方式

  • 在本地开启服务端
  • 需要有 Java 环境,配置参考:百度安全验证
    • 下载地址:Index of java-local/jdk/8u201-b09
  • Linux & Mac:bin/sekiro.sh 双击打开服务端
  • Windows:bin/sekiro.bat 双击打开服务端

2.客户端环境

  • 地址:file.virjar.com/sekiro_web_client.js?_=123 这个地址是在前端创建客户端的时候需要用到的代码,Sekiro-RPC 把他封装在一个地址里面了

3.使用参数说明

  • 使用原理:客户端注入到浏览器环境,然后通过SekiroClient和 Sekiro服务器通信,即可直接 RPC 调用浏览器内部方法,官方提供的SekiroClient 代码样例如下:
// 生成唯一标记uuid编号
function guid() {function S4() {return (((1+Math.random())*0x10000)|0).toString(16).substring(1);}return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}
// 连接服务端
var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=ws-group&clientId="+guid());
// 业务接口 
client.registerAction("登陆",function(request, resolve, reject){resolve(""+new Date());
})
 
  • group:业务类型(接口组),每个业务一个 group,group 下面可以注册多个终端(SekiroClient),同时group 可以挂载多个 Action;
  • clientId:指代设备,多个设备使用多个机器提供 API 服务,提供群控能力和负载均衡能力;
  • SekiroClient:服务提供者客户端,主要场景为手机/浏览器等。最终的 Sekiro 调用会转发到 SekiroClient。每个client 需要有一个惟一的 clientId;
  • registerAction:接口,同一个 group 下面可以有多个接口,分别做不同的功能;
  • resolve:将内容传回给服务端的方法;
  • request:服务端传过来的请求,如果请求里有多个参数,可以以键值对的方式从里面提取参数然后再做处理。

2. 测试使用

1. 前端代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
​
<script src="http://file.virjar.com/sekiro_web_client.js?_=123"></script>
<script>function guid() {function S4() {return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);}
​return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());}
​var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=rpc-test&clientId=" + guid());
​client.registerAction("clientTime", function (request, resolve, reject) {resolve("" + new Date());})
​
</script>
</body>
</html>

2. SK API

Sekiro 为我们提供了一些 API

  • 查看分组列表:http://127.0.0.1:5620/business-demo/groupList
  • 查看队列状态:http://127.0.0.1:5620/business-demo/clientQueue?group=rpc-test
  • 调用转发:http://127.0.0.1:5620/business-demo/invoke?group=rpc-test&action=clientTime

3.python调用代码

import requests
​
params = {"group": "rpc-test","action": "clientTime",
}
res = requests.get("http://127.0.0.1:5620/business-demo/invoke", params=params)
print(res.text)
​

3.案例

案例还是上一个

首先打开服务端

  • 确定好位置之后我们就可以替换当前文件,把我们需要的rpc的代码给注入到网页中
  • JavaScript代码
(function () {function SekiroClient(wsURL) {this.wsURL = wsURL;this.handlers = {};this.socket = {};this.base64 = false;// checkif (!wsURL) {throw new Error('wsURL can not be empty!!')}this.webSocketFactory = this.resolveWebSocketFactory();this.connect()}SekiroClient.prototype.resolveWebSocketFactory = function () {if (typeof window === 'object') {var theWebSocket = window.WebSocket ? window.WebSocket : window.MozWebSocket;return function (wsURL) {function WindowWebSocketWrapper(wsURL) {this.mSocket = new theWebSocket(wsURL);}WindowWebSocketWrapper.prototype.close = function () {this.mSocket.close();};WindowWebSocketWrapper.prototype.onmessage = function (onMessageFunction) {this.mSocket.onmessage = onMessageFunction;};WindowWebSocketWrapper.prototype.onopen = function (onOpenFunction) {this.mSocket.onopen = onOpenFunction;};WindowWebSocketWrapper.prototype.onclose = function (onCloseFunction) {this.mSocket.onclose = onCloseFunction;};WindowWebSocketWrapper.prototype.send = function (message) {this.mSocket.send(message);};return new WindowWebSocketWrapper(wsURL);}}if (typeof weex === 'object') {// this is weex env : https://weex.apache.org/zh/docs/modules/websockets.htmltry {console.log("test webSocket for weex");var ws = weex.requireModule('webSocket');console.log("find webSocket for weex:" + ws);return function (wsURL) {try {ws.close();} catch (e) {}ws.WebSocket(wsURL, '');return ws;}} catch (e) {console.log(e);//ignore}}//TODO support ReactNativeif (typeof WebSocket === 'object') {return function (wsURL) {return new theWebSocket(wsURL);}}// weex 鍜� PC鐜鐨剋ebsocket API涓嶅畬鍏ㄤ竴鑷达紝鎵€浠ュ仛浜嗘娊璞″吋瀹�throw new Error("the js environment do not support websocket");};SekiroClient.prototype.connect = function () {console.log('sekiro: begin of connect to wsURL: ' + this.wsURL);var _this = this;// 涓峜heck close锛岃// if (this.socket && this.socket.readyState === 1) {//     this.socket.close();// }try {this.socket = this.webSocketFactory(this.wsURL);} catch (e) {console.log("sekiro: create connection failed,reconnect after 2s");setTimeout(function () {_this.connect()}, 2000)}this.socket.onmessage(function (event) {_this.handleSekiroRequest(event.data)});this.socket.onopen(function (event) {console.log('sekiro: open a sekiro client connection')});this.socket.onclose(function (event) {console.log('sekiro: disconnected ,reconnection after 2s');setTimeout(function () {_this.connect()}, 2000)});};SekiroClient.prototype.handleSekiroRequest = function (requestJson) {console.log("receive sekiro request: " + requestJson);var request = JSON.parse(requestJson);var seq = request['__sekiro_seq__'];if (!request['action']) {this.sendFailed(seq, 'need request param {action}');return}var action = request['action'];if (!this.handlers[action]) {this.sendFailed(seq, 'no action handler: ' + action + ' defined');return}var theHandler = this.handlers[action];var _this = this;try {theHandler(request, function (response) {try {_this.sendSuccess(seq, response)} catch (e) {_this.sendFailed(seq, "e:" + e);}}, function (errorMessage) {_this.sendFailed(seq, errorMessage)})} catch (e) {console.log("error: " + e);_this.sendFailed(seq, ":" + e);}};SekiroClient.prototype.sendSuccess = function (seq, response) {var responseJson;if (typeof response == 'string') {try {responseJson = JSON.parse(response);} catch (e) {responseJson = {};responseJson['data'] = response;}} else if (typeof response == 'object') {responseJson = response;} else {responseJson = {};responseJson['data'] = response;}if (typeof response == 'string') {responseJson = {};responseJson['data'] = response;}if (Array.isArray(responseJson)) {responseJson = {data: responseJson,code: 0}}if (responseJson['code']) {responseJson['code'] = 0;} else if (responseJson['status']) {responseJson['status'] = 0;} else {responseJson['status'] = 0;}responseJson['__sekiro_seq__'] = seq;var responseText = JSON.stringify(responseJson);console.log("response :" + responseText);if (responseText.length < 1024 * 6) {this.socket.send(responseText);return;}if (this.base64) {responseText = this.base64Encode(responseText)}//澶ф姤鏂囪鍒嗘浼犺緭var segmentSize = 1024 * 5;var i = 0, totalFrameIndex = Math.floor(responseText.length / segmentSize) + 1;for (; i < totalFrameIndex; i++) {var frameData = JSON.stringify({__sekiro_frame_total: totalFrameIndex,__sekiro_index: i,__sekiro_seq__: seq,__sekiro_base64: this.base64,__sekiro_is_frame: true,__sekiro_content: responseText.substring(i * segmentSize, (i + 1) * segmentSize)});console.log("frame: " + frameData);this.socket.send(frameData);}};SekiroClient.prototype.sendFailed = function (seq, errorMessage) {if (typeof errorMessage != 'string') {errorMessage = JSON.stringify(errorMessage);}var responseJson = {};responseJson['message'] = errorMessage;responseJson['status'] = -1;responseJson['__sekiro_seq__'] = seq;var responseText = JSON.stringify(responseJson);console.log("sekiro: response :" + responseText);this.socket.send(responseText)};SekiroClient.prototype.registerAction = function (action, handler) {if (typeof action !== 'string') {throw new Error("an action must be string");}if (typeof handler !== 'function') {throw new Error("a handler must be function");}console.log("sekiro: register action: " + action);this.handlers[action] = handler;return this;};SekiroClient.prototype.encodeWithBase64 = function () {this.base64 = arguments && arguments.length > 0 && arguments[0];};SekiroClient.prototype.base64Encode = function (s) {if (arguments.length !== 1) {throw "SyntaxError: exactly one argument required";}s = String(s);if (s.length === 0) {return s;}function _get_chars(ch, y) {if (ch < 0x80) y.push(ch);else if (ch < 0x800) {y.push(0xc0 + ((ch >> 6) & 0x1f));y.push(0x80 + (ch & 0x3f));} else {y.push(0xe0 + ((ch >> 12) & 0xf));y.push(0x80 + ((ch >> 6) & 0x3f));y.push(0x80 + (ch & 0x3f));}}var _PADCHAR = "=",_ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",_VERSION = "1.1";//Mr. Ruan fix to 1.1 to support asian char(utf8)//s = _encode_utf8(s);var i,b10,y = [],x = [],len = s.length;i = 0;while (i < len) {_get_chars(s.charCodeAt(i), y);while (y.length >= 3) {var ch1 = y.shift();var ch2 = y.shift();var ch3 = y.shift();b10 = (ch1 << 16) | (ch2 << 8) | ch3;x.push(_ALPHA.charAt(b10 >> 18));x.push(_ALPHA.charAt((b10 >> 12) & 0x3F));x.push(_ALPHA.charAt((b10 >> 6) & 0x3f));x.push(_ALPHA.charAt(b10 & 0x3f));}i++;}switch (y.length) {case 1:var ch = y.shift();b10 = ch << 16;x.push(_ALPHA.charAt(b10 >> 18) + _ALPHA.charAt((b10 >> 12) & 0x3F) + _PADCHAR + _PADCHAR);break;case 2:var ch1 = y.shift();var ch2 = y.shift();b10 = (ch1 << 16) | (ch2 << 8);x.push(_ALPHA.charAt(b10 >> 18) + _ALPHA.charAt((b10 >> 12) & 0x3F) + _ALPHA.charAt((b10 >> 6) & 0x3f) + _PADCHAR);break;}return x.join("");};function startRpc() {if (window.flag) {} else {function guid() {function S4() {return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);}return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());}// 创建一个标记用来判断是否创建套接字window.flag = true;var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=rpc-test&clientId=" + guid());client.registerAction("ths", function (request, resolve, reject) {resolve(rt.update());})}}setTimeout(startRpc, 1000)
})()

别看这么长,实际要改的代码只有

 var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=rpc-xhs&clientId=" + guid());client.registerAction("xhs", function (request, resolve, reject) {var data=request['data'];// console.log(data)data=JSON.parse(data)console.log(data)var res=window._webmsxyw('/api/sns/web/v1/homefeed',data)res=JSON.stringify(res)resolve(res);

这样既开启成功了

接下来调用即可

代码

具体看知识星球

效果


http://www.ppmy.cn/devtools/122423.html

相关文章

Stable Diffusion绘画 | 插件-Deforum:商业LOGO广告视频

第1步&#xff1a;在 Deforum 的「初始化」中上传需要展示的 LOGO图片&#xff1a; 第2步&#xff1a;在「运行」模块进行设置&#xff1a; 设置宽高&#xff1a;保持与初始化上传图片的像素一致 设置迭代步数&#xff1a;步数越高&#xff0c;视频细节越好 其他参数保持默认…

微服务seata解析部署使用全流程

官网地址&#xff1a; Seata 是什么&#xff1f; | Apache Seata 1、Seata术语 用来管理分布式事务&#xff0c;由阿里巴巴出品。 【1、TC (Transaction Coordinator) - 事务协调者】 用来维护事务的&#xff0c;包括主事务和分支事务。 【2、TM (Transaction Manager) - …

【MySQL】查询原理 —— B+树查询数据全过程

使用B树作为索引结构的原因&#xff1a; 一种自平衡树&#xff1a; B树在插入和删除的时候节点会进行分裂和合并操作&#xff0c;以保持树的平衡&#xff0c;存在冗余节点&#xff0c;使得删除的时候树结构变化小&#xff0c;更高效。 高度不会增长过快&#xff0c;查询磁盘I…

项目-坦克大战学习-游戏结束

当boos受到伤害时游戏结束&#xff0c;游戏结束时我们需要将窗体全部绘制从别的画面&#xff0c;这样我们可以在游戏运行类中的update设置条件&#xff0c;在游戏运行类thread创建一个枚举类型定义是否游戏结束 public enum Game { play, over };//定义现在游戏运行状态 如果…

贪心算法.

序幕 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在求解问题时采取逐步构建解决方案的策略&#xff0c;每一步都选择当前状态下局部最优的解&#xff0c;期望通过局部最优解能够得到全局最优解。 以上为了严谨性&#xff0c;引用了官方用语。 而用大白话总结就是&…

《RabbitMQ篇》Centos7安装RabbitMQ

安装RabbitMQ 安装包网盘下载地址 链接&#xff1a;https://pan.baidu.com/s/1bG_nP0iCdAejkctFp1QztQ?pwd4mlw 先上传安装包到服务器&#xff08;erlang-23.3.4.11-1.el7.x86_64.rpm和rabbitmq-server-3.9.16-1.el7.noarch.rpm&#xff09;然后使用指令安装 # 安装 erlang r…

uniapp中实现评分组件,多用于购买商品后,对商品进行评价等场景

前言 uni-rate是uniapp框架中提供的一个评分组件。它可以用于用户评价、打分等场景。uni-rate组件可以根据设定的星星总数&#xff0c;展示用户评分的效果&#xff0c;用户可以通过点击星星或滑动星星的方式进行评分。同时&#xff0c;uni-rate组件也支持自定义星星图标、星星…

数据结构之AVL树(万字详解)

目录 一、什么是AVL树 1.1 AVL树介绍 1.2 AVL树的结构 二、AVL树的插入 2.1 插入过程 2.2 更新平衡因子 三、AVL树的旋转 3.1 单旋思路 3.2 单旋代码 3.3 为什么要双旋 3.4 双旋思路 3.5 双旋代码 四、C源代码 一、什么是AVL树 1.1 AVL树介绍 AVL树是一种自平衡…