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

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

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

这样既开启成功了

接下来调用即可

代码

具体看知识星球

效果

相关文章:

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

知识星球&#xff1a;知识星球 | 深度连接铁杆粉丝&#xff0c;运营高品质社群&#xff0c;知识变现的工具知识星球是创作者连接铁杆粉丝&#xff0c;实现知识变现的工具。任何从事创作或艺术的人&#xff0c;例如艺术家、工匠、教师、学术研究、科普等&#xff0c;只要能获得一…...

uni-app之旅-day06-加入购物车

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言8.0 创建 cart 分支8.1 配置 vuex8.2 创建购物车的 store 模块8.3 在商品详情页中使用 Store 中的数据8.4 实现加入购物车的功能8.5 动态统计购物车中商品的总数…...

【Kubernetes】常见面试题汇总(五十六)

目录 123. pod 创建失败&#xff1f; 124. kube-flannel-ds-amd64-ndsf7 插件 pod 的 status 为 Init:0/1 &#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#x…...

LabVIEW激光诱导击穿光谱识别与分析系统

LabVIEW激光诱导击穿光谱&#xff08;LIBS&#xff09;分析系统利用高能量脉冲激光产生高温等离子体&#xff0c;通过分析等离子体发出的光谱来定性分析样品中的元素种类。该系统的开发集成了软件与硬件的设计&#xff0c;实现了自动识别和定性分析功能&#xff0c;适用于环境监…...

Redis的基础篇

Redis的基础篇 1.在CentOs7上安装Redis&#xff08;最好不要在windows上装&#xff0c;版本少&#xff09; 1.安装gcc --> yum install gcc tcl(可能会报错&#xff0c;重新安装yum就行了) 2.下载redis --> 最好是6.2上的版本 3.解压redis --> tar -zxvf redis-6.2.…...

java和python哪个好

Java和Python各有优缺点&#xff0c;适合不同的应用场景&#xff0c;具体看你需要在哪种情况下使用编程语言。以下是Java和Python的一些对比&#xff0c;帮助你决定哪种更适合你的需求&#xff1a; 性能 Java&#xff1a;编译型语言&#xff0c;编译成字节码运行在Java虚拟机&…...

Electron + ts + vue3 + vite

正常搭建脚手架&#xff1a;npm create vitelatest 项目名称 安装electron的相关依赖&#xff1a;注&#xff1a;安装时终端url要项目名那一层 安装npm install electron -D安装打包工具&#xff1a;npm install electron-builder -D开发工具&#xff1a;npm install electron-…...

《大规模语言模型从理论到实践》第一轮学习--分布式训练

基础知识 5分钟看懂电脑硬件配置 - 知乎 (zhihu.com) 显存 定义&#xff1a;显存是显卡上的专用高速缓存&#xff0c;用于存储图形处理器&#xff08;GPU&#xff09;在处理图像和视频数据时所需的临时数据。 功能&#xff1a;显存的主要作用是提供GPU快速访问的数据存储&a…...

多模态智能

研究背景&#xff1a; 深度学习从1.0的端到端走向2.0的预训练&#xff0c;通过大规模预训练来记忆多模态数据中共性知识&#xff0c;增强对下游任务的学习能力。 深度学习1.0&#xff1a;特定任务有标注训练数据->随机初始化训练->最终模型 深度学习2.0&#xff1a;大规…...

【机器学习(十三)】机器学习回归案例之股票价格预测分析—Sentosa_DSML社区版

文章目录 一、背景描述二、Python代码和Sentosa_DSML社区版算法实现对比(一) 数据读入(二) 特征工程(三) 样本分区(四) 模型训练和评估(五) 模型可视化 三、总结 一、背景描述 股票价格是一种不稳定的时间序列,受多种因素的影响。影响股市的外部因素很多,主要有经济因素、政治因…...

大模型微调

概述 什么是模型微调&#xff1f; 模型微调是通过微调工具&#xff0c;使用独特的场景数据对平台的基础模型进行调整&#xff0c;帮助你快速定制一个更符合业务需求的大型模型。其优势在于对基础模型进行小幅调整以满足特定需求&#xff0c;相比于训练一个新模型&#xff0c;…...

240607 继承

面向对象三大特性&#xff1a;封装、继承、多态 RE: 封装 C把数据和方法封装在类里面迭代器和适配器 继承 1 基类 & 派生类 一个类可以派生自多个类&#xff0c;这意味着&#xff0c;它可以从多个基类继承数据和函数。定义一个派生类&#xff0c;我们使用一个类派生列表…...

轻松应对意外丢失:高效电脑数据恢复指南!

有时候由于误操作、硬件故障、病毒攻击等原因&#xff0c;电脑里的重要文件可能会突然消失不见。面对这样的情况&#xff0c;很多人会感到手足无措。其实&#xff0c;借助专业的电脑数据恢复软件&#xff0c;我们可以较为轻松地找回丢失的数据。今天&#xff0c;我们就来介绍几…...

vue项目中播放rtsp视频流

一、下载webrtc-streamer 下载地址&#xff1a;https://github.com/mpromonet/webrtc-streamer/releases 根据设备型号下载对应的版本到本地直接解压就行&#xff0c;我下载的是webrtc-streamer-v0.8.6-dirty-Windows-AMD64-Release.tar版本。 双击webrtc-streamer.exe可执行文…...

tomcat部署web配置环境变量

在Tomcat中设置环境变量通常涉及以下步骤&#xff1a; 找到Tomcat的启动脚本&#xff08;如catalina.sh或catalina.bat&#xff09;。 在启动脚本中设置环境变量。 对于catalina.sh&#xff08;Linux/Unix系统&#xff09;&#xff0c;你可以在文件顶部添加环境变量&#xf…...

数据仓库技术及应用(练习1)

1.创表 &#xff08;1&#xff09;customers.csv CREATE EXTERNAL TABLE IF NOT EXISTS customers ( customer_id int, customer_fname varchar(45), customer_lname varchar(45), customer_email varchar(45), customer_password varchar(45), customer_street …...

老板的“神助攻”:公司电脑监控软件

在当今的商业世界中&#xff0c;企业管理者都希望员工能全身心投入工作&#xff0c;为企业创造更多价值。然而&#xff0c;员工上班摸鱼的现象却让许多老板头疼不已。公司电脑监控软件的出现&#xff0c;为解决这一问题提供了可能。接下来&#xff0c;我们将详细介绍几款优质的…...

前端vue部署网站

这里讲解一下前端vue框架部署网站&#xff0c;使用工具是 xshell 和 xftp &#xff08;大家去官网安装免费版的就行了&#xff09; 服务器 我使用的阿里云服务器&#xff0c;买的是 99 一年的&#xff0c;淘宝有新手9.9 一个月服务器。可以去用&#xff0c;学生的话是有免费三…...

Unity3D 动画回调函数详解

在Unity3D中&#xff0c;动画回调函数是实现精细动画效果的重要工具。通过动画回调函数&#xff0c;我们可以在动画的特定时刻执行自定义代码&#xff0c;从而实现更加灵活和复杂的动画效果。本文将详细解释Unity3D中的动画回调函数&#xff0c;并提供相应的代码实现。 对惹&a…...

el-table表格表尾合计行,指定合计某几列,自定义合计方法

&#x1f935; 作者&#xff1a;coderYYY &#x1f9d1; 个人简介&#xff1a;前端程序媛&#xff0c;目前主攻web前端&#xff0c;后端辅助&#xff0c;其他技术知识也会偶尔分享&#x1f340;欢迎和我一起交流&#xff01;&#x1f680;&#xff08;评论和私信一般会回&#…...

一款工具替你解决Mac电脑菜单栏图标杂乱问题

你的菜单栏是不是各种图标挤在一起&#xff1f;图标过多显得杂乱&#xff1f;刘海屏遮挡菜单栏图标&#xff1f;教你如何让你的菜单栏变的简洁美观 iBar&#xff0c;一款Mac上优秀的菜单栏管理工具&#xff0c;可以自主选择菜单栏图标隐藏&#xff0c;单独窗口聚合展示&#x…...

MySQL 基础入门教程

参考视频地址&#xff1a;一小时MySQL教程 bilibili SQL 基础 数据库分为关系型数据库和非关系型数据库 常见的关系型数据库&#xff1a; MySQL、PostgreSQL、Oracle、SQL Server等。 非关系型数据库&#xff1a; MongoDB&#xff08;文档型数据库&#xff09;、Redis&am…...

俏生元将传统膳食智慧融入现代生活,自然成分绽放健康光彩

近年来&#xff0c;当代女性健康食品市场正经历快速发展和显著变化。随着女性健康意识的提升&#xff0c;市场对专门针对女性健康的产品需求快速上升。女性消费者对健康的关注不再局限于表面&#xff0c;而是越来越注重内在健康和生活质量的提升。此外&#xff0c;中式养生文化…...

腾讯云推流播放相关

直播的在线人数是否有上限&#xff1f; 腾讯云直播默认不限制观看直播的在线人数&#xff0c;只要网络等条件允许都可以观看直播。如果用户配置了带宽限制&#xff0c;当观看人数过多、超出了限制带宽时新的用户无法观看&#xff0c;此情况下在线人数是有限制的。 如何使用播…...

UE5运行时动态加载场景角色动画任意搭配-相机及运镜(二)

通过《MMD模型及动作一键完美导入UE5》系列文章,我们可以把外部场景、角色、动画资产导入UE5,接下来我们将实现运行时动态加载这些资产,并任意组合搭配。 1、运行时播放相机动画 1、创建1个BlueprintActor,通过这个蓝图动态创建1个LevelSequence,并Play 2、将这个Bluep…...

@JsonAlias和@JSONField序列化和反序列化

com.fasterxml.jackson.annotation.JsonAlias("expressCode") com.alibaba.fastjson.annotation.JSONField(name "expressCode") 这两个注解分别属于不同的JSON序列化框架&#xff1a;Jackson 和 Fastjson&#xff0c;它们的用途是处理JSON字段的名称映射…...

k8s1.27部署ingress 1.11.2

k8s1.27部署ingress 1.11.2 要求&#xff1a; 1、使用主机网络。 2、多节点部署&#xff0c;以来标签&#xff1a;isingressistrue ingress1.11.2支持版本 官方参考链接&#xff1a; https://github.com/kubernetes/ingress-nginx/ 官网yaml https://raw.githubuserconten…...

【运维】自动化运维详解

目录 引言一、什么是自动化运维&#xff1f;二、自动化运维的优势三、自动化运维的关键组成部分详解3.1 监控与告警3.2 部署与配置管理3.3 备份与恢复3.4 安全管理 总结 引言 在当今信息技术飞速发展的时代&#xff0c;企业对IT基础设施的依赖日益增强&#xff0c;传统的人工运…...

线控底盘技术介绍

随着汽车工业的不断发展&#xff0c;传统的机械控制系统逐渐向电子控制系统转变。线控底盘&#xff08;Drive-by-Wire Chassis&#xff09;作为这一转变的重要组成部分&#xff0c;正在改变汽车的操控方式和驾驶体验。本文将全面介绍线控底盘的概念、组成、工作原理、优缺点、应…...

DOM对象

DOM概述 官方定义&#xff1a; DOM是W3C制定的一个规范(标准)&#xff0c;(Document Object Model&#xff0c;文档对象模型)&#xff0c;是提供了访问和操作网页中各元素的方法&#xff0c;让程序可以动态的修改或改变网页元素的内容、样式、结构。 DOM是W3C制定的一个规范…...