web端 -- RPC - 通用方案

背景

当一个目标站加密字段特别多,而且我们对其并不需要大规模爬取时,考虑到时间成本,我们就可以使用RPC来获取加密数据。

原理: 搭建一个后端服务器, 然后在浏览器中hook 加密函数,注册到web.js端中,最后通过client端来回调加密函数,实现rpc远程调用

以上就是整个RPC 获取加密数据的流程。

自己封装了一个通用框架, 使用时只需修改一个点就行:

  1. 自定义 client 端的 type
  2. 自定义 web 端 type
  3. client 端的 callback 值 —》 web 端 type
  4. web 端 callback 值 —》 client 端 type
  5. web 端的加密函数注册
  6. client 端的 method 值为 web 端注册的加密

简单的demo

server.py
# 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))

client.py
# 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)

web.js
/*
* 示例:
    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));
    }
}();

启动顺序:

  1. server
  2. web
  3. client

你可能感兴趣的:(爬虫,爬虫)