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

news2024/10/4 13:11:57

知识星球:知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具知识星球是创作者连接铁杆粉丝,实现知识变现的工具。任何从事创作或艺术的人,例如艺术家、工匠、教师、学术研究、科普等,只要能获得一千位铁杆粉丝,就足够生计无忧,自由创作。社群管理、内容沉淀、链接粉丝等就在知识星球。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.com
    const 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 -> value
        var text = document.getElementById('box').value
        // 客户端发信息发服务器
        websocket.send(text)
    }

</script>
</body>
</html>

2.服务端

# encoding: utf-8
import asyncio
import websockets


async def echo(websocket):
    # 使用WebSocket在客户端和服务器之间建立全双工双向连接后,就可以在连接打开时调用send()方法。
    message = 'hello world'
    # 发送数据
    await websocket.send(message)
    return True


async 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 json


headers = {
    "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;
        // check
        if (!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.html
            try {
                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 ReactNative
        if (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.coloradmin.cn/o/2188066.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

lambda表达式底层实现:反编译LambdaMetafactory + 转储dump + 运行过程 + 反汇编 + 动态指令invokedynamic

一、结论先行 lambda 底层实现机制 1.lambda 表达式的本质&#xff1a;函数式接口的匿名子类的匿名对象 2.lambda表达式是语法糖 语法糖&#xff1a;编码时是lambda简洁的表达式&#xff0c;在字节码期&#xff0c;语法糖会被转换为实际复杂的实现方式&#xff0c;含义不变&am…

低空无人机飞手四类超视距无人机技术详解

低空无人机飞手中的四类超视距无人机技术详解&#xff0c;主要涉及无人机的性能特点、技术要求、培训内容以及应用场景等方面。以下是对这些方面的详细阐述&#xff1a; 一、四类无人机&#xff08;中型无人机&#xff09;性能特点 四类无人机&#xff0c;现已更名为中型无人…

OpenCAEPoro优化(2)

前言&#xff1a; 首先有一点要注意&#xff1a; 修改代码时&#xff0c;要注意命名空间的冲突问题&#xff08;主要是头文件中&#xff09; 作者了解了相关这个项目的一些背景介绍&#xff1b;得到的主要信息是&#xff1a;这种大型程序一般都是优化的比较完善了&#xff0…

【Vue3实战】:用导航守卫拦截未保存的编辑,提升用户体验

前言 在Vue3应用中&#xff0c;用户可能会在一个页面上进行数据编辑&#xff0c;如填写表单或修改表格中的数据。当用户在未保存更改的情况下尝试离开当前页面时&#xff0c;我们希望能够弹出提示框&#xff0c;告知用户有未保存的更改&#xff0c;并询问是否确定离开。 一、使…

【案例】平面云

教程案例视频&#xff1a;Unity Shader Graph - 云教程 开发平台&#xff1a;Unity 2022 开发工具&#xff1a;Unity ShaderGraph   一、效果展示 二、ShaderGraph 路线图 三、案例分析 核心思路&#xff1a;使用 Noise&#xff08;噪声&#xff09;模拟云层状态   3.1 说明…

打造高效灵活的数字企业——The Open Group 2024生态系统架构·可持续发展年度大会重磅来袭

随着数字经济的高速发展&#xff0c;企业数字化转型已成为时代的必然趋势。如何在这场变革中抢占先机&#xff0c;实现业务增长与降本增效&#xff0c;成为众多企业关注的焦点。为此&#xff0c;The Open Group 2024生态系统架构可持续发展年度大会将于明年盛大开启&#xff0c…

Studying-多线程学习Part1-线程库的基本使用、线程函数中的数据未定义错误、互斥量解决多线程数据共享问题

来源&#xff1a;多线程编程 线程库的基本使用 两个概念&#xff1a; 进程是运行中的程序线程是进程中的进程 串行运行&#xff1a;一次只能取得一个任务并执行这一个任务 并行运行&#xff1a;可以同时通过多进程/多线程的方式取得多个任务&#xff0c;并以多进程或多线程…

Leetcode: 0011-0020题速览

Leetcode: 0011-0020题速览 本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer&#xff08;第 2 版&#xff09;》、《程序员面试金典&#xff08;第 6 版&#xff09;》题解 遵从开源协议为知识共享 版权归属-相同方式…

Java在用增强for循环遍历集合时删除元素,抛出java.util.ConcurrentModificationException异常

文章目录 0. 前言1. 问题产生的背景2. Java中增强for循环的底层原理3. 为什么增强for循环不支持在遍历集合时删除元素3.1 问题排查3.2 modCount 变量的来源3.3 expectedModCount 变量的来源3.4 导致modCount变量和expectedModCount不相等的原因3.5 为什么用迭代器遍历元素时删除…

学籍管理平台|在线学籍管理平台系统|基于Springboot+VUE的在线学籍管理平台系统设计与实现(源码+数据库+文档)

在线学籍管理平台系统 目录 基于SpringbootVUE的在线学籍管理平台系统设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大…

Leetcode: 0021-0030题速览

Leetcode: 0021-0030题速览 本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer&#xff08;第 2 版&#xff09;》、《程序员面试金典&#xff08;第 6 版&#xff09;》题解 遵从开源协议为知识共享 版权归属-相同方式…

如此的“宠物医保”靠谱吗?

今天是世界动物日&#xff0c;本“人民体验官”推广人民日报官方微博文化产品《带着笑意的眼睛&#xff0c;能看见最美的风景》。 截图&#xff1a;来源“人民体验官”推广平台 人民微博说&#xff0c;带着笑意的眼睛&#xff0c;能看见最美的风景。生活中多一点微笑&#xff…

buuctf 部分misc题

rar 这个不多说了直接暴力破解&#xff0c;提示说的已经很清楚了&#xff0c;四位数密码&#xff1b; qr 这个就是一个二维码&#xff0c;用qr扫一下就出来了&#xff1b; 镜子里面的世界 用stegslove&#xff0c;打开dataextract进行调整 调整之后就可以得到flag了 ning…

ipv6之ospf配置

topo图 路由器均使用ipv6的地址 AR1使用默认路由到达ISP&#xff0c;在ospf上使用路由下发功能&#xff0c;把ISP的静态路由下发给内部网络 ISP使用7条静态路由&#xff0c;到达内部网络 AR1、AR2、AR3、AR4之间使用ospfv3进行通信 但是我配了下面的代码无法通信&#xff0…

driver,sequencer,sequence之间的握手关系_2024年10月3日

driver &#xff1a;根据接口协议将事务转换为一组信号级切换的组件。 sequencer &#xff1a;将事务&#xff08;sequence items&#xff09;从 sequence 发送到 driver&#xff0c;并将 driver 的响应反馈给 sequence 的组件。会对同时尝试访问 driver 以激励设计接口的多个 …

什么是 HTTP 请求中的 options 请求?

在 Chrome 开发者工具中的 Network 面板看到的 HTTP 方法 OPTIONS&#xff0c;其实是 HTTP 协议的一部分&#xff0c;用于客户端和服务器之间进行“预检”或“协商”。OPTIONS 请求的作用是让客户端能够获取关于服务器支持的 HTTP 方法和其他跨域资源共享 (CORS) 相关的信息&am…

spring揭秘25-springmvc04-servlet容器与springmvc容器总结

文章目录 【README】【1】DelegatingFilterProxy回顾【1.1】DelegatingFilterProxy初始化过滤器bean 【2】从servlet容器获取springmvc顶级web容器【2.1】从Servlet容器中获取springmvc容器总结【2.2】ContextLoaderListener加载springmvc顶级web容器并将其添加到servlet容器【…

基于SpringBoot+Vue的科研课题项目管理系统源码

文章目录 1.技术架构2.主要功能3.获取方式 1.技术架构 后端&#xff1a;SpringBoot 前端&#xff1a;Vue – Element UI 2.主要功能 登录 /注销、 用户管理、项目管理、申报管理、变更管理、 结题管理、角色管理、权限管理、数据字典等功能 3.获取方式 点击下方名片&a…

android 全面屏最底部栏沉浸式

Activity的onCreate方法中添加 this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); Android 系统 Bar 沉浸式完美兼容方案自 Android 5.0 版本&#xff0c;Android 带来了沉浸式系统 ba - 掘金 (juejin.cn)https://juejin.cn/post/7075578…

pycharm中使用anaconda创建多环境,无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称

问题描述 用的IDE是&#xff1a; 使用anaconda创建了一个Python 3.9的环境 结果使用pip命令的时候&#xff0c;报错 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称 解决方案 为了不再增加系统变量&#xff0c;我们直接将变量添加在当前项目中你的Ter…