Files
GenericAgent/memory/tmwebdriver_sop.md

123 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TMWebDriver SOP
- 禁止import直接用web_scan/web_execute_js工具。本文件只记录特性和坑。
- 底层:`../TMWebDriver.py`通过Chrome扩展(非Tampermonkey)接管用户浏览器(保留登录态/Cookie
- 非Selenium/Playwright保留用户浏览器登录态
- ⚠扩展更新后旧tab的content script不重载→需刷新页面
## 通用特性
- ⚠web_execute_js里使用`await`时需**显式`return`**才能拿到返回值底层async包裹不写return则返回null
- ✅web_scan自动穿透同源iframe跨域iframe需CDP或postMessage见下方章节
## 限制(isTrusted)
- JS事件`isTrusted=false`,敏感操作(如文件上传/部分按钮)可能被拦截;这类场景首选**CDP桥**
- ⚠JS点击按钮打不开新tab→可能是浏览器弹窗拦截换CDP点击试试
- 文件上传JS无法填充`<input type=file>`首选CDP batchgetDocument→querySelector→DOM.setFileInputFiles备选ljqCtrl物理点击
- 需转物理坐标时:`physX = (screenX + rect中心x) * dpr``physY = (screenY + chromeH + rect中心y) * dpr`;其中 `chromeH = outerHeight - innerHeight`
## 导航
- `web_scan` 仅读当前页不导航,切换网站用 `web_execute_js` + `location.href='url'`
## Google图搜
- class名混淆禁硬编码点击结果用 `[role=button]` div
- web_scan过滤边栏弹出后用JS文本`document.body.innerText`大图遍历img按`naturalWidth`最大取src
- "访问"链接遍历a找`textContent.includes('访问')`的href
- 缩略图:`img[src^="data:image"]`直接提取大图src可能截断用`return img.src`
## Chrome下载PDF
场景PDF链接在浏览器内预览而非下载
```js
fetch('PDF_URL').then(r=>r.blob()).then(b=>{
const a=document.createElement('a');
a.href=URL.createObjectURL(b);
a.download='filename.pdf';
a.click();
});
```
注意需同源或CORS允许跨域先导航到目标域再执行
## Chrome后台标签节流
- 后台标签中`setTimeout`被Chrome intensive throttling延迟到≥1min/次扩展脚本中避免依赖setTimeout轮询
## CDP桥(tmwd_cdp_bridge扩展) ⭐首选
扩展路径:`assets/tmwd_cdp_bridge/`(需安装含debugger权限)
⚠TID约定标识首次运行自动生成到`assets/tmwd_cdp_bridge/config.js`(已gitignore)扩展通过manifest引用
调用:`web_execute_js` script直传JSON字符串工具层自动识别对象格式走WS→background.js cmd路由
```js
// 直接传JSON字符串作为script参数无需DOM操作
web_execute_js script='{"cmd": "cookies"}'
web_execute_js script='{"cmd": "tabs"}'
web_execute_js script='{"cmd": "cdp", "tabId": N, "method": "...", "params": {...}}'
web_execute_js script='{"cmd": "batch", "commands": [...]}'
// 返回值直接是JSON结果
```
通信方式⭐JSON字符串直传(首选) | TID DOM方式(TID元素+MutationObserverweb_scan/execute_js底层依赖)
单命令:`{cmd:'tabs'}` | `{cmd:'cookies'}` | `{cmd:'cdp', tabId:N, method:'...', params:{...}}` | `{cmd:'management', method:'list|reload|disable|enable', extId:'...'}`
- managementlist返回所有扩展信息reload/disable/enable需传extId
- ⭐batch混合`{cmd:'batch', commands:[{cmd:'cookies'},{cmd:'tabs'},{cmd:'cdp',...},...]}`
- 返回`{ok:true, results:[...]}`一次请求多命令CDP懒attach复用session
- 子命令会自动继承外层batch的tabId如cookies命令可正确获取当前页面URL
- `$N.path`引用第N个结果字段(0-indexed),如`"nodeId":"$2.root.nodeId"`
- ⚠batch前序命令失败时后续`$N`引用会静默变成undefined要检查results数组中每项的ok状态
- 典型文件上传getDocument(**depth:1**) → querySelector(`input[type=file]`) → setFileInputFiles
- 思想:
- 同一链路内保持nodeId来源一致不混用querySelector路径与performSearch路径
- 上传后前端框架可能不感知必要时JS补发`input`/`change`事件
- 上传前检查`input.accept`多input时用accept/父容器语义区分
- 等待元素优先用`DOM.performSearch('input[type=file]')`做轻量轮询
- 瞬态input的核心是**缩短发现→setFileInputFiles时间窗**优先同batch完成再不行用DOM事件监听猴子补丁仅作兜底思路
- ⚠tabIdCDP默认sender.tab.id(当前注入页)跨tab需显式tabId或先batch内tabs查
- ⭐跨tab无需前台指定tabId即可操作后台标签页
## CDP点击完整生命周期未验证BBS#23
- 通用点击需**三事件序列**mouseMoved → mousePressed → mouseReleased间隔50-100ms
- 省略mouseMoved会导致MUI Tooltip/Ant Design Dropdown等hover依赖组件失效
- ⚠autofill释放是特例只需mousePressed即可见下方autofill章节
- 坐标修正页面有transform:scale/zoom时
```js
var scale = window.visualViewport ? window.visualViewport.scale : 1;
var zoom = parseFloat(getComputedStyle(document.documentElement).zoom) || 1;
var realX = x * zoom; var realY = y * zoom;
```
- iframe内元素CDP点击坐标需合成 `finalX = iframeRect.x + elRect.x`
- 跨域iframe拿不到contentDocument
- ⚠`Target.getTargets`/`Target.attachToTarget`在CDP桥中返回"Not allowed"(chrome.debugger权限限制)
- ⭐**已验证方案**`Page.getFrameTree`找iframe frameId → `Page.createIsolatedWorld({frameId})`获取contextId → `Runtime.evaluate({expression, contextId})`在iframe中执行JS
- batch链式引用`$0.frameTree.childFrames`遍历找url匹配的frame`$1.executionContextId`传给evaluate
- postMessage中继方案仅在content script已注入iframe时有效第三方支付iframe通常无注入
## CDP文本输入未验证BBS#23
- `insertText`快但无key事件受控组件需补dispatch `input`事件
- 需完整键盘模拟时用`dispatchKeyEvent`逐键派发
## CDP DOM域穿透 closed Shadow DOM未验证BBS#24/#25
- `DOM.getDocument({depth:-1, pierce:true})` 穿透所有Shadow边界含closed
- `DOM.querySelector({nodeId, selector})` 定位 → `DOM.getBoxModel({nodeId})` 取坐标
- getBoxModel返回content八值[x1,y1,...x4,y4],中心用**四点平均**centerX=sum(x)/4, centerY=sum(y)/4
- ⚠不能简化为对角线平均——元素有transform:rotate/skew时四点非矩形
- querySelector**不能跨Shadow边界写组合选择器**需分步先找host再在其shadow内找子元素
- ⚠nodeId在DOM变更后失效 → 用`backendNodeId`更稳定或重新getDocument刷新
## autofill获取与登录
检测web_scan输出input带`data-autofilled="true"`value显示为受保护提示(非真实值Chrome安全保护需点击释放)
- ⚠**前置条件必须先CDP `Page.bringToFront` 切tab到前台**Chrome仅在前台tab释放autofill保护值后台tab物理点击无效
- ⭐**一键释放与登录**bringToFront → mousePressed点任一字段(无需Released一个释放全页) → 等500ms → 补input/change事件 → 点登录
## 验证码/页面视觉截图
- ⭐首选CDP截图`Page.captureScreenshot`(format:'png')→返回base64无需前台/后台tab也行全页高清
- 验证码canvas/imgJS `canvas.toDataURL()` 直接拿base64最干净
## simphtml与TMWebDriver调试
- simphtml调试必须通过`code_run`注入JS到真实浏览器Python端无法模拟DOM
- `d=TMWebDriver()`, `d.set_session('url_pattern')`, `d.execute_js(code)` → 返回`{'data': value}`
- simphtml`str(simphtml.optimize_html_for_tokens(html))` — 返回BS4 Tag需str()
- ⚠**DOMRect坑(hasOverlap)**:某些上下文`rect.x/y`为undefined(只有left/top)导致NaN→误判重叠。兼容`rect.x ?? rect.left`
## 连不上排查
web_scan失败时按序排查
①扩展没装→检查Chrome扩展列表(chrome://extensions)是否有TMWebDriver扩展
没找到→走web_setup_sop找到→确认已启用
②浏览器没开?→检查①对应的浏览器进程是否在跑(tasklist/ps)没有则启动并打开正常URL⚠about:blank等内部页不加载扩展
③WS后台挂了→socket.connect_ex(('127.0.0.1',18766))非0即dead→手动`from TMWebDriver import TMWebDriver; TMWebDriver()`起master