"""真人拖动轨迹监视器:等 rmc iframe 出现后 attach,每 0.5s 快照 window.__dragTrace / __lastVerify 到 /tmp,iframe 销毁(验证通过)后保留最后快照退出。""" import asyncio, json, time, urllib.request as ur import websockets def _json(path): try: with ur.urlopen(f"http://127.0.0.1:9222{path}", timeout=5) as r: return json.load(r) except (OSError, ValueError): return None async def main(): ver = _json("/json/version") if not ver or not ver.get("webSocketDebuggerUrl"): print("CDP 不可达,退出") return # 等待验证码 iframe 出现(最多 180s) ifr = None for _ in range(60): targets = _json("/json/list") or [] ifr = next((t for t in targets if "rmc.bytedance.com" in t.get("url", "")), None) if ifr: break await asyncio.sleep(3) if not ifr: print("无 iframe,退出") return async with websockets.connect(ver["webSocketDebuggerUrl"], max_size=64 * 1024 * 1024) as ws: mid = [0] async def cdp(method, params=None, sid=None): mid[0] += 1 m = {"id": mid[0], "method": method, "params": params or {}} if sid: m["sessionId"] = sid await ws.send(json.dumps(m)) while True: try: r = json.loads(await ws.recv()) except (json.JSONDecodeError, asyncio.TimeoutError): continue if r.get("id") == mid[0]: if "error" in r: raise RuntimeError(str(r["error"])) return r.get("result", {}) isid = (await cdp("Target.attachToTarget", {"targetId": ifr["id"], "flatten": True}))["sessionId"] await cdp("Runtime.enable", sid=isid) print("trace_watch 就绪", flush=True) last_len = -1 while True: try: r = await cdp("Runtime.evaluate", { "expression": "JSON.stringify({n:(window.__dragTrace||[]).length," "trace:window.__dragTrace||null,v:window.__lastVerify||null})", "returnByValue": True}, sid=isid) val = r.get("result", {}).get("value") if val: d = json.loads(val) if d["n"] != last_len: last_len = d["n"] with open("/tmp/trace_snap.json", "w") as f: json.dump(d, f) if d["v"]: with open("/tmp/verify_snap.txt", "w") as f: f.write(d["v"]) print(f"[{time.strftime('%H:%M:%S')}] {d['n']} 事件", flush=True) await asyncio.sleep(0.5) except (RuntimeError, websockets.exceptions.ConnectionClosed, OSError): print("iframe 已销毁,保留最后快照退出", flush=True) return asyncio.run(main())