引言:
JS 爬虫,绕不过瑞数这道坎,卡的死死的。一般网上的教程就是补环境什么的,我尝试了,可以但是比较麻烦。 今天说一种,秒过的方式,抗并发。那就是牛逼的RPC,hook JS 技术。
前期准备:
- 安装nodejs
下载地址:
Node.js — Run JavaScript Everywhere
安装依赖
npm install ws
npm install express
- Google浏览器
F12 调试,会反反调试
- 代码1,NodeJS 端
完成1,组装http服务,提供对外接口,方便其他地方直接调用. 2 ,监控websocket 服务,与hook 的js 交互,http服务连接websocket,websocket 连接hookjs, js 直接调用网站的协议。
代码:
global.navigator = {appName: 'nodejs'}; // fake the navigator object
global.window = {}; // fake the window object
const express=require('express');
const server=express();
const bodyParser = require('body-parser');
server.listen(8093,'0.0.0.0',function(){
console.log('http://localhost:8093');
});
server.use(bodyParser.urlencoded({ extended: false })); //调用中间件
server.use(bodyParser.json())
//import * as WebSocket from 'ws';
const WebSocket = require('ws')
const server1 = new WebSocket.Server({ port: 8013 });
console.log('begin')
server1.on('connection', (socket) => {
console.log('New client connected');
socket.on('message', (message) => {
console.log(`Received message: ${message}`);
try {
// 向所有客户端广播消息
if(message.indexOf("ping") >=0 ) {
server1.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send("ping");
}
});
}else if(message.indexOf("rep") >=0 ) {
// global.retmsg = message;
str = ` ${message}`
var args = str.split('|');
uuid = args[2]
var tmpjs = JSON.parse(args[1])
global.resps[uuid].json({code:1,message:'',data:tmpjs,uuid:uuid})
global.resps[uuid] = null;
// global.resp.json({code:1,message:'',data:str})
}
}catch(error) {
console.log(error);
}
});
socket.on('close', () => {
console.log('Client disconnected');
});
});
global.resps = {}
function guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
global.server = server1
server.post('/list',function(req,resp){
console.log("list 接收参数:" + req.body + new Date());
//global.resp = undefined;
// uuid = guid();
uuid = req.body.uuid;
server1.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
global.retmsg = "";
client.send("list|"+JSON.stringify(req.body) +"|" + uuid);
}
});
global.resps[uuid] = resp
}
);
const WebSocket = require('ws')
const server1 = new WebSocket.Server({ port: 8013 });
监听 websocket 端口 8013
这个是心跳包,网页js 与 nodejs 保持连接,保持心跳用
// 向所有客户端广播消息
if(message.indexOf("ping") >=0 ) {
server1.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send("ping");
}
});
}
// 这个是定义的websocket 返回,接收到websocket 返回,然后发送给 http 客户端。
}else if(message.indexOf("rep") >=0 ) {
// global.retmsg = message;
str = ` ${message}`
var args = str.split('|');
uuid = args[2]
var tmpjs = JSON.parse(args[1])
global.resps[uuid].json({code:1,message:'',data:tmpjs,uuid:uuid})
global.resps[uuid] = null;
// global.resp.json({code:1,message:'',data:str})
}
// 这里定义一个对外http 服务,list就是 http://localhost:8093/list , 参数json格式
{ “data”:”11”,”uuid”:”333”}, 有一个全局变量 global.resps, 他这样存储 global.resps[uuid] = resp 一个uuid 即一个http客户端的变量,方便从websocke他得到返回之后,直接给他发送消息。
client.send("list|"+JSON.stringify(req.body) +"|" + uuid); ,这个是http 收到数据,直接发送给websocket 即js 客户端就行。
server.post('/list',function(req,resp){
console.log("list 接收参数:" + req.body + new Date());
uuid = req.body.uuid;
server1.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
global.retmsg = "";
client.send("list|"+JSON.stringify(req.body) +"|" + uuid);
}
});
global.resps[uuid] = resp
}
);
- 代码2, js hook 端
- 1 先来看下一个实例网站。
启动器,定位到请求位置所在。
//跟踪进去代码,是直接发送ajax,那一分钟就搞定。瑞数就是说他会hook ajax send 函数,添加头,添加cookie 之类的。我们搞不顶瑞数,但是可以写ajax 请求啊
2.2 js 端代码:
function patch_ruishu(url){
console.log("RPC args:"+url);
var v = [];
v.push(window.hc_url(url));
v.push(window.hc_cook(945, 10,false));
return v;
}
// 闭包
!function (a) {
// Promise是一种用于处理异步操作的机制
// 该函数接受两个参数:resolve和reject。
// resolve是一个函数,用于将Promise从挂起状态转换为已解决状态(fulfilled),并返回解决结果(通常是异步操作的结果)。
// reject是一个函数,用于将Promise从挂起状态转换为已拒绝状态(rejected),并返回拒绝原因(通常是异步操作的错误信息)。
new Promise((e, s) => {
e(a)
}
).then((myFunction) => {
var cbb_MyId = "1001";
// 执行下面语句之后,客户端就会与服务器进行连接。
ws = new WebSocket("ws://127.0.0.1:8013");
// 指定连接成功后的回调函数
ws.onopen = function () {
console.log("服务器连接成功!");
// send 向服务器发送数据
setInterval(function () {
ws.send(cbb_MyId+'ping---'+"connect");
}, 5000)
}
;
window.ws = ws;
// 收到服务器数据后的回调函数。
ws.onmessage = function (evt) {
if (evt.data.indexOf("ping") >= 0) {
console.log('ping')
return;
}
console.log('传入数据: ' + evt.data);
if(evt.data.indexOf("list") >=0 ) {
//调用方法
var args = evt.data.split('|');
console.log(args[1]);
var reqdata = JSON.parse(args[1]);
try {
var serviceNbr = reqdata.mobile;
var param ="userInfo.loginType=4&userInfo.serviceNbr="+serviceNbr+"&flag=0&chargeType=undefined"
var code = mycode(serviceNbr.replace(/ /g,""),"7");
param=param+"&key="+code;
$.ajax({
type:'POST',
url:"https://ah.189.cn/service/pay/findByUser.action",
data:param,
dataType:'json',
cache:false,
//async:false,
success:function (data) {
if(data.flag == '0'){
ws.send("rep|"+ JSON.stringify(data)+ "|" + args[2])
}else {
ws.send("rep|"+ JSON.stringify(data)+ "|" + args[2])
}
}
});
}catch(e) {
console.log(e)
}
$.ajax({
type: "POST",
contentType: "application/x-www-form-urlencoded;charset=utf-8",
url: path + "/service/index/getDistrictBargain.action",
dataType: "json",
success: function(data) {
var listDistrictBargain = data.listDistrictBargain;
var page = data.page;
ws.send("rep|"+ JSON.stringify(page)+ "|" + args[2])
},
error: function() {
}
});
}
};
// 连接关闭后的回调函数
ws.onclose = function () {
console.log("rpc已经断开了哦");
};
// 连接错误后的回调函数
ws.onerror = function () {
console.log("rpc已经断开了哦");
};
}
)
;
}(patch_ruishu) // 这里填返回加密数据的js函数名字
初始化websocket ,然后连上刚才的nodejs
ws = new WebSocket("ws://127.0.0.1:8013");
// 指定连接成功后的回调函数
ws.onopen = function () {
console.log("服务器连接成功!");
// send 向服务器发送数据
setInterval(function () {
ws.send(cbb_MyId+'ping---'+"connect");
}, 5000)
}
;
- 收到请求:
if(evt.data.indexOf("list") >=0 ) {
//调用方法
var args = evt.data.split('|');
console.log(args[1]);
var reqdata = JSON.parse(args[1]);
try {
var serviceNbr = reqdata.mobile;
var param ="userInfo.loginType=4&userInfo.serviceNbr="+serviceNbr+"&flag=0&chargeType=undefined"
var code = mycode(serviceNbr.replace(/ /g,""),"7");
param=param+"&key="+code;
$.ajax({
type:'POST',
url:"https://ah.189.cn/service/pay/findByUser.action",
data:param,
dataType:'json',
cache:false,
//async:false,
success:function (data) {
if(data.flag == '0'){
ws.send("rep|"+ JSON.stringify(data)+ "|" + args[2])
}else {
ws.send("rep|"+ JSON.stringify(data)+ "|" + args[2])
}
}
});
}catch(e) {
console.log(e)
}
$.ajax({
type: "POST",
contentType: "application/x-www-form-urlencoded;charset=utf-8",
url: path + "/service/index/getDistrictBargain.action",
dataType: "json",
success: function(data) {
var listDistrictBargain = data.listDistrictBargain;
var page = data.page;
ws.send("rep|"+ JSON.stringify(page)+ "|" + args[2])
},
error: function() {
}
});
}
- Node js 远程
- C# 远程。
- Js websocket 注意事项 如何hook, 如何页面reload ,如何替换js
高级防止 掉线问题,Ip 问题 ,浏览器指纹问题 抗并发,抗造。 如何生产部署,快速集成到csharpcef 框架