ClericPy / ichrome

Chrome controller for Humans, based on Chrome Devtools Protocol(CDP) and python3.7+.
https://pypi.org/project/ichrome/
MIT License
227 stars 29 forks source link

waitevent会不会漏掉消息 #107

Closed zenghh closed 1 year ago

zenghh commented 1 year ago
            while True:
                self.tab_my_ws.wait_event(
                    "Network.webSocketFrameReceived",
                    callback_function=self.on_my_ws_frame,
                )
                time.sleep(0)

我这样使用的话,有没有可能websocket的消息遗漏处理

ClericPy commented 1 year ago

最好还是直接走 Tab 的 DefaultCallback 吧. 你这是协程的么

zenghh commented 1 year ago

不是协程, sync函数

zenghh commented 1 year ago

要怎么做才能不遗漏呢,我发现应该是有遗漏掉部分消息

ClericPy commented 1 year ago

sync 部分我基本都不维护了, 缺点太多折腾不过来, 如果想监听所有事件, 可以魔改 Tab._recv_daemon 里面加上个默认回调应该就可以了

PS: 以前开源这套框架其实是因为没其他可用的, 我记得微软的 play Wright 也是支持同步和协程两套方案的, 可以考虑试一试, 虽然 puppeteer 作者都去了微软开发那个库, 但我看了下 python 版本的似乎只是调用了 node.js 的各类方法... 源码不是纯 python 的

zenghh commented 1 year ago

sync 部分我基本都不维护了, 缺点太多折腾不过来, 如果想监听所有事件, 可以魔改 Tab._recv_daemon 里面加上个默认回调应该就可以了

PS: 以前开源这套框架其实是因为没其他可用的, 我记得微软的 play Wright 也是支持同步和协程两套方案的, 可以考虑试一试, 虽然 puppeteer 作者都去了微软开发那个库, 但我看了下 python 版本的似乎只是调用了 node.js 的各类方法... 源码不是纯 python 的

我之前也用playwright,但是我要爬取的网站可以检测出来用自动化框架,所以借用下你的

ClericPy commented 1 year ago

呃... 好几个用了别的被检测跑来用我这个了... 我也没加屏蔽指纹之类的东西, 怪了. 你可以先猴子补丁覆盖下那个函数试试吧, 我也不确定会不会被检测到. 有啥问题随时交流

zenghh commented 1 year ago

你的可以正常运行,还有你的async版本会有问题吗,能否接收到全部监听的websocket消息

ClericPy commented 1 year ago

大概我的只添加了几个比较简单的默认启动参数, 没加太多. 其实你直接魔改 tab 下面的 _recv_daemon 函数加上回调自己处理就可以的, 遗漏消息主要是 while 循环里等待是有间隔的, 魔改方法对动态语言来说也不麻烦

当然也可以不改源码试试, 你的代码里把回调函数去掉放到 filter_function 参数里面, 然后把超时开高, filter_function 里面一直返回 false 它就会一直收消息, 不过也不保证不丢消息...毕竟在循环里面是有空档的, 不如上面魔改

协程的一直在维护, 而且自带 Default Callback 方法, 当初弃用多线程主要还是多线程无法在外部杀死, 而且并发高起来以后 CPU 切换成本太高(有时候要维护集群里的远程 chrome)

zenghh commented 1 year ago

主要asyncio不是太理解,所以只好用sync版本

ClericPy commented 1 year ago

没事, 先解决问题, 有空再看. 协程也不万能的, 要改整套架构都要改太费劲了, 一不小心还容易因为一个 CPU 秘籍任务阻塞主线程, 用多线程就不会.

早年间用 gevent 时候被一个 C 写的 mysql 库阻塞的整个服务都慢了十倍, 不熟悉还是不能轻易上

zenghh commented 1 year ago
    def wait_event(
        self,
        event="",
        timeout=None,
        callback_function=None,
        filter_function=None,
        wait_seconds=None,
    ):
        """ensure enable the method first, or will not listen any event."""
        timeout = self.timeout if timeout is None else timeout
        start_time = time.time()
        while 1:
            request = {"method": event}
            result = self.recv(request, timeout=timeout)
            timeout_break = wait_seconds and time.time(
            ) - start_time > wait_seconds
            if timeout_break:
                break
            if result or timeout == 0:
                if callable(filter_function):
                    if filter_function(result):
                        break
                else:
                    break
        return callback_function(result) if callable(
            callback_function) else result

应该是wait_seconds 调高,然后filter_funciton 返回false吧

zenghh commented 1 year ago

大概我的只添加了几个比较简单的默认启动参数, 没加太多. 其实你直接魔改 tab 下面的 _recv_daemon 函数加上回调自己处理就可以的, 遗漏消息主要是 while 循环里等待是有间隔的, 魔改方法对动态语言来说也不麻烦

这边能不能麻烦你把代码加上去,我好更新代码

ClericPy commented 1 year ago
from ichrome import Chrome, Tab
import json
import websocket

def _recv_daemon(self):

    while self.ws.connected:
        try:
            data_str = self.ws.recv()
            if not data_str:
                continue
            try:
                data_dict = json.loads(data_str)
                if not isinstance(data_dict, dict):
                    continue
            except (TypeError, json.decoder.JSONDecodeError):
                continue
            callback(data_dict)
            f = self._listener.find_future(data_dict)
            if f:
                f.set_result(data_str)
        except (
                websocket._exceptions.WebSocketConnectionClosedException,
                websocket._exceptions.WebSocketTimeoutException,
                ConnectionResetError,
        ):
            break

def callback(data):
    # 或者丢队列里去
    print('自定义函数', data)

Tab._recv_daemon = _recv_daemon

chrome = Chrome()
tab = chrome.new_tab()
# 记着打开这个 domain, 否则看不到事件
tab.enable('Network')
tab.set_url('http://bing.com')
print(tab.title)

就这么魔改一下原来的函数就行了, 最近有点忙

zenghh commented 1 year ago

谢谢