当一个目标站加密字段特别多,而且我们对其并不需要大规模爬取时,考虑到时间成本,我们就可以使用RPC来获取加密数据。
原理: 搭建一个后端服务器, 然后在浏览器中hook 加密函数,注册到web.js端中,最后通过client端来回调加密函数,实现rpc远程调用
以上就是整个RPC 获取加密数据的流程。
自己封装了一个通用框架, 使用时只需修改一个点就行:
# coding: utf-8
import asyncio
import json
import websockets
from websockets.exceptions import ConnectionClosedError
"""
server 服务, 作为web端与采集端的中转, server端不做逻辑处理。
server 调用模型:
请求数据类型必须是 JSON型 的字符串, 如:
json.dumps({
'type': 'crawl1',
'callback': 'web_key|crawl_key',
'data': {"xx": "123", "xxx": "456"}
})
:type 自身函数回调key
:data 传向 web|crawl 的数据
:callback web|crawl 的type key
error code:
402: 请求参数类型错误
403: web|crawl 端不存在
500: web|crawl 端掉线
"""
# web 端与采集端任务记录
callback_connect = {}
async def send_to_all(message, key):
"""
转发函数
:param message: 转发信息体
:param key: callback key
:return:
"""
try:
await callback_connect[key].send(message)
except ConnectionClosedError:
# 对于掉线的 web 进行清理
del callback_connect[key]
data = json.loads(message)
callback_key = data.get('type')
data['code'] = 500
data['message'] = 'web 端已断开连接'
await callback_connect[callback_key].send(json.dumps(data))
if 'crawl' in key:
del callback_connect[key]
async def main_logic(websocket):
print(websocket)
while True:
try:
recv_text = await websocket.recv()
except Exception:
continue
# 获取数据
try:
resp_info = json.loads(recv_text)
except Exception:
message = json.dumps({
'code': 402,
'data': f'request data type is error, data of json!'
})
await websocket.send(message)
continue
_type = resp_info.get('type')
callback_key = resp_info.get('callback')
if not callback_key:
message = json.dumps({
'code': 403,
'data': f'not find callback: {callback_key} of client'
})
await websocket.send(message)
continue
# 初始化
if callback_key == 'init':
callback_connect[_type] = websocket
continue
# 采集程序注册-》 callback
if not callback_connect.get(_type) and _type.startswith('crawl'):
callback_connect[_type] = websocket
if callback_key == 'close':
try:
del callback_connect[_type]
except Exception:
pass
print("dddd ----- ", callback_connect)
continue
# 错误的callback
if not callback_connect.get(callback_key):
message = json.dumps({
'code': 403,
'data': f'not find callback client: {callback_key}'
})
await websocket.send(message)
continue
await send_to_all(recv_text, callback_key)
async def main(ip, prot):
async with websockets.serve(main_logic, ip, prot, ping_interval=None):
# run forever
await asyncio.Future()
if __name__ == '__main__':
host = "127.0.0.1"
_prot = 9999
asyncio.run(main(host, _prot))
# coding: utf-8
import asyncio
import websockets
import json
"""
采集端服务, 必须注意交互数据格式
示例:
send_info = {
'type': '',
'callback': '',
'method': '',
'data': '',
'code': 200,
}
: type 自身函数回调key
: callback web端的type key
: method 要调用的加密方法
: data 调用加密方法传入的数据
: code 状态码
"""
send_info = {
'type': '',
'callback': '',
'method': '',
'data': '',
'code': 200,
}
async def ws_client(url, params):
async with websockets.connect(url, ping_interval=None) as websocket:
send_info['type'] = 'crawl'
send_info['callback'] = 'web1'
send_info['method'] = 'encrypt2'
send_info['data'] = params
send_info['code'] = 200
msg = json.dumps(send_info)
await websocket.send(msg)
while True:
response = await websocket.recv()
resp_data = json.loads(response)
break
return resp_data
if __name__ == "__main__":
ss = '这是crawl'
data = asyncio.run(ws_client('ws://127.0.0.1:9999', ss))
print(data)
/*
* 示例:
send_info = {
'type': '',
'callback': '',
'method': '',
'data': '',
'code': 200,
}
: type 自身函数回调key
: callback web端的type key
: method 要调用的加密方法
: data 调用加密方法传入的数据
: code 状态码
* */
let WebSocket = require('ws')
// 交互数据类型
let send_info = {
"type": "",
"callback": "",
"method": "",
"data": "",
"code": "",
};
function encrypt1() {
// 自定义返回数据类型: string、json
return 'encrypt1'
}
function encrypt2() {
return 'encrypt2'
}
// 注册 - 加密函数
invoke_function = {
'encrypt1': encrypt1,
'encrypt2': encrypt2,
}
!function () {
// 更换成你的server ip and port
let ws = new WebSocket("ws://127.0.0.1:9999");
ws.onopen = function () {
// 建立连接后触发 - 初始化
send_info["type"] = "web1"
send_info["callback"] = "init"
// 返回数据
ws.send(JSON.stringify(send_info));
};
ws.onmessage = function (evt) {
// 接收服务端返回信息
let resp_data = JSON.parse(evt.data);
let data = resp_data['data'];
let callback = resp_data['type'];
// 获取 - 加密函数名称 && 执行加密
let value;
let method_name = resp_data['method'];
if (data != '') {
value = invoke_function[method_name](data);
send_info["code"] = 200;
}else {
value = invoke_function[method_name]();
send_info["code"] = 200;
}
send_info["type"] = "web1";
send_info["callback"] = callback;
send_info["method"] = method_name;
// 加密结果由加密函数控制
send_info["data"] = value;
// 返回加密结果
ws.send(JSON.stringify(send_info));
}
}();
启动顺序: