- method_l_shape: OK 候选尺度 ±0.0375 精修(步长 0.0125 + ±6px 窗口), GT live_165204 Δ 179→176(真人 177),离线 9/10 保持 - 插桩 JSON.stringify 捕获 SDK 日志明文;指针 probe 排除 coalesced/pressure - 拖动不跟手指标作废:post_pos 晚于 SDK 重置滑块,鼠标事件流始终完整 - 剩余 VerifyErr = 候选整体选错(视觉确证),方向:多候选质量评估 - docs: experiments/solution/retrospective 同步收尾
78 lines
3.0 KiB
Python
78 lines
3.0 KiB
Python
"""真人拖动轨迹监视器:等 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())
|