1. 这个拾取器到底解决了什么问题先说背景。最近在折腾 Playwright MCP 做浏览器自动化的时候遇到一个很具体的痛点我需要快速拿到页面上某个元素的稳定选择器然后喂给自动化脚本或者 AI 工具去执行后续操作。官方那套方案当时还没提供元素拾取的能力社区里能搜到的要么是 Chrome DevTools 里手动 Copy selector要么是写一段document.querySelector在控制台里试来试去。前者拿到的选择器经常是div div:nth-child(3) span这种一改版就废的路径后者效率太低点一下换个元素就得重来一遍。所以我花了一个晚上写了这个 219 行的元素拾取器。核心目标很明确鼠标悬停高亮、点击即复制、生成的选择器要足够稳定、输出格式直接兼容 Playwright MCP 和常见 AI 编码工具的输入要求。后来发现官方在 5 天后才放出类似功能社区里也有人问“browser use MCP 跟 playwright MCP 有什么区别”其实在元素拾取这个场景下区别就在于你能不能拿到一个语义化、可复用的选择器而不是一堆脆弱的 CSS 路径。这个工具适合几类人正在用 Playwright 做自动化测试或爬虫的开发者、需要给 AI Agent 提供页面元素定位能力的工程师、以及任何想快速获取网页元素选择器但不想装一堆浏览器插件的朋友。219 行不算多但每一行都是踩过坑之后留下来的。2. 整体设计思路与方案选型2.1 为什么不用 Chrome 扩展而选择注入脚本最直接的方案当然是写个 Chrome 扩展装上去就能用。但我实际试过之后放弃了原因有三个。第一扩展需要打包、加载、处理权限申请分享给别人用的时候对方还得开开发者模式门槛太高。第二扩展的 content script 和页面脚本之间存在隔离环境拿到的元素引用有时候和页面实际执行环境不一致调试起来很烦。第三我最终的使用场景是把拾取结果直接喂给 Playwright MCP而 MCP 本身就是在页面上下文里执行脚本的所以直接在页面里注入一段 JS 反而更自然。最终方案就是一段自包含的 JavaScript通过page.evaluate()或者直接在 DevTools Console 里粘贴执行。219 行的体积控制也是有意为之——太大了不好维护太小了功能不够。我试过压缩到 150 行以内但发现选择器生成逻辑和 UI 反馈逻辑必须分开写才清晰强行合并只会让后面改起来痛苦。2.2 选择器生成策略稳定优先于简短这是整个工具最核心的部分。很多人写元素拾取器的时候只想着“能选中就行”但实际用起来你会发现选择器的稳定性比简短重要得多。我见过太多人用nth-child生成的选择器页面一改版全部失效然后就得重新拾取一遍。我的策略是优先级从高到低>const picker (() { let active false; let overlay null; let currentEl null; const config { outputFormat: css, // css | json | playwright highlightColor: rgba(59, 130, 246, 0.3), borderColor: #3b82f6 }; // ... 后续逻辑 return { start, stop, getResult }; })(); picker.start();脚本注入后页面上会出现一个小的提示条告诉你拾取模式已激活。这时候移动鼠标元素会被蓝色半透明层高亮。点击任意元素选择器会被复制到剪贴板同时在 Console 里打印出来。4.2 选择器生成的核心代码拆解选择器生成函数是整个工具的心脏我把它拆成几个步骤。第一步是收集候选属性按优先级排序。第二步是逐个尝试看哪个能唯一定位。第三步是如果都不行就走路径兜底。function generateSelector(el) { const candidates []; // 优先级1data-testid 类属性 for (const attr of [data-testid, data-test, data-cy, data-qa]) { const val el.getAttribute(attr); if (val) candidates.push([${attr}${val}]); } // 优先级2唯一 id if (el.id document.querySelectorAll(#${CSS.escape(el.id)}).length 1) { candidates.push(#${CSS.escape(el.id)}); } // 优先级3语义化 class 组合 const semanticClasses Array.from(el.classList).filter(isSemanticClass); if (semanticClasses.length 0) { const classSelector el.tagName.toLowerCase() . semanticClasses.join(.); if (document.querySelectorAll(classSelector).length 1) { candidates.push(classSelector); } } // 优先级4路径兜底 candidates.push(buildPath(el)); return candidates[0]; }isSemanticClass的判断逻辑是过滤掉哈希类名和纯数字类名。我用的正则大致是/^[a-z][a-z0-9]*(-[a-z0-9])*$/同时排除长度超过 20 的太长的通常是自动生成的和包含连续数字超过 4 位的。4.3 路径兜底算法的实现当所有语义化选择器都失败时就需要构建一条从根节点到目标元素的路径。这个路径要尽量短同时保证唯一性。我的做法是从目标元素往上走每次尝试用当前节点的 tag class 组合来定位如果唯一就停止否则继续往上。function buildPath(el) { const parts []; let current el; while (current current ! document.body) { let part current.tagName.toLowerCase(); if (current.id) { part #${CSS.escape(current.id)}; parts.unshift(part); break; } const parent current.parentElement; if (parent) { const siblings Array.from(parent.children).filter( c c.tagName current.tagName ); if (siblings.length 1) { const index siblings.indexOf(current) 1; part :nth-of-type(${index}); } } parts.unshift(part); current current.parentElement; } return parts.join( ); }这个算法有个优化点如果中途遇到有 id 的祖先节点就直接用 id 作为锚点停止往上走。因为 id 通常是唯一的从 id 往下构建路径比从 body 往下要短得多可读性也好很多。4.4 输出格式切换与结果处理拾取完成后结果会以你配置的格式输出。CSS 格式就是纯字符串JSON 格式包含更多元信息Playwright 格式会尝试生成getByRole或getByText。function formatResult(el, selector) { if (config.outputFormat json) { return JSON.stringify({ selector, tag: el.tagName.toLowerCase(), text: el.textContent.trim().slice(0, 100), id: el.id || null, classes: Array.from(el.classList), rect: el.getBoundingClientRect().toJSON() }, null, 2); } if (config.outputFormat playwright) { const role el.getAttribute(role) || inferRole(el); const text el.textContent.trim().slice(0, 50); if (role text) { return getByRole(${role}, { name: ${text} }); } return locator(${selector}); } return selector; }inferRole是一个简单的推断函数根据标签名映射到 ARIA role。比如button映射到buttona映射到linkinput[typetext]映射到textbox。这个推断不追求 100% 准确够用就行。5. 常见问题与排查技巧实录5.1 拾取器不生效的几种情况最常见的问题是脚本注入后没有任何反应。排查顺序是这样的先看 Console 有没有报错如果有SyntaxError说明代码被截断了重新完整复制一遍。如果没有报错但也没反应检查页面是否有 CSP 限制某些严格 CSP 的页面会阻止内联脚本执行这时候需要通过 Playwright 的page.addScriptTag()来注入。还有一种情况是页面用了 Shadow DOM普通的事件监听拿不到 Shadow 内部的元素。这个场景我的工具没有覆盖因为实现成本太高而且大多数需要拾取元素的场景都是普通 DOM。如果你确实需要处理 Shadow DOM可以在事件监听里加composedPath()来判断。注意某些页面会监听mousemove并做自己的高亮或 tooltip可能会和我的覆盖层冲突。如果发现高亮层闪烁或者位置不对可以先把页面上其他高亮插件关掉再试。5.2 选择器不稳定的典型场景有时候拾取到的选择器当时能用过几天页面改版就失效了。这通常是因为选择器依赖了nth-child或者哈希类名。我的工具已经尽量避免了但有一种情况无法完全规避页面用 CSS-in-JS 且所有类名都是哈希同时元素没有 id 和 data 属性。这种情况下只能走路径兜底稳定性天然就差。应对策略是尽量拾取那些有语义锚点的元素。比如你要操作一个提交按钮不要直接拾取按钮本身而是拾取它所在的表单容器然后用form button[typesubmit]这种方式定位。这样即使按钮的类名变了只要表单结构没变选择器就还有效。5.3 与 AI 工具对接时的格式问题把拾取结果喂给 AI 工具时最常见的坑是 JSON 格式不对。有些工具要求严格的 JSON不能有尾逗号不能有单引号。我的JSON.stringify输出是标准格式但如果你手动修改过结果记得用json校验工具过一遍。热词里有人问json parse error: cannot deserialize value of type java.util.Date from String这类问题通常是日期格式不匹配和元素拾取本身无关但提醒我们在构造 JSON 时要注意字段类型的一致性。另一个坑是选择器里的特殊字符转义。如果元素的 id 或 class 里包含冒号、方括号、点号直接拼进选择器会解析失败。必须用CSS.escape()处理。我见过有人拾取一个 id 为user:name的元素生成的选择器是#user:name结果querySelector直接报错。加上CSS.escape之后就变成#user\:name正常工作。5.4 常见问题速查表问题现象可能原因解决方法脚本注入后无反应CSP 限制或代码截断用addScriptTag注入或重新复制完整代码高亮层位置偏移页面有 transform 或 zoom检查getBoundingClientRect是否受 CSS transform 影响点击后页面跳转事件拦截不完整确保mousedown、mouseup、click都拦截选择器不唯一页面有重复 id 或 class检查生成逻辑必要时手动加nth-of-type复制到剪贴板失败浏览器权限限制改用 Console 输出手动复制Shadow DOM 内元素选不中事件未穿透 Shadow 边界使用composedPath()或改用 Playwright 的 pierce 选择器6. 一些实操心得与扩展思路这个工具我前后改了七八版最大的体会是元素拾取器的价值不在于“能选中”而在于“选得稳”。一个只能选中一次的选择器毫无意义你需要的是那种页面小改版之后依然能用的选择器。所以我在选择器生成上花的精力远大于 UI 和高亮。另一个心得是关于输出格式的。一开始我只输出 CSS 选择器字符串后来发现很多人需要把拾取结果存下来做批量处理纯字符串不够用才加了 JSON 格式。再后来发现 AI 工具更喜欢语义化的定位方式又加了 Playwright 格式。如果你也在做类似工具建议一开始就把输出格式设计成可扩展的不然后面加格式要改很多地方。扩展方向有几个。一是支持批量拾取按住 Shift 点击多个元素一次性生成一组选择器。二是支持选择器验证拾取完成后自动在页面上跑一遍确认能唯一定位再输出。三是和 Playwright MCP 深度集成拾取完直接生成可执行的 MCP 命令。这几个我都在陆续做但核心的 219 行已经够用了。最后分享一个小技巧如果你经常需要拾取同一类元素可以在配置里预设一组属性优先级。比如你的项目里所有可测试元素都有>
企业数字化 ERP 产品动态
相关推荐
PHP文库系统源码:多站点部署与Sphinx全文检索实践 简介:面向计划搭建或运营文库网站的开发者与站长,知沃文库系统基于Yii2框架开发,融合百度文库、原创力、豆丁文库等平台思路,提供文档转换预览、全文检索、支付、私域流量、小程序公众号及商城资讯分销等运营模块。资源包共1个PDF… · 2026/11/3 21:57:56
导师退回问题清单:论文从开题到定稿,哪些 AI 工具能真正帮上忙 每年看本科生、研究生的开题报告和论文初稿,最常被退回的,通常不是“句子不够华丽”,而是下面三类问题:
选题、参考文献和引用格式出问题:题目过大、研究问题不清;文献像是临时凑的;引文格式前后… · 2026/11/3 21:57:56
后端任务管理模块设计与实践指南 1. 任务管理模块的设计背景与核心价值在任何一个后端系统中,任务管理模块都是支撑业务运转的中枢神经。我经历过三个不同行业的后端系统开发,发现无论电商、社交还是企业OA系统,任务管理模块的设计质量直接决定了系统的可靠性和扩展性。这个看… · 2026/11/3 21:56:55
带宽被谁吃光?终端用 bandwhich 快速定位指南 带宽被谁吃光?终端用 bandwhich 快速定位指南 【免费下载链接】bandwhich Terminal bandwidth utilization tool 项目地址: https://gitcode.com/gh_mirrors/ba/bandwhich
半夜,服务器出口带宽被顶满,你翻接口统计却查不到是谁干的。打… · 2026/11/4 15:57:52
Qwen3-VL-8B:如何用AI实现视觉推理与智能交互? Qwen3-VL-8B:如何用AI实现视觉推理与智能交互? 【免费下载链接】Qwen3-VL-8B-Thinking-unsloth-bnb-4bit 项目地址: https://ai.gitcode.com/hf_mirrors/unsloth/Qwen3-VL-8B-Thinking-unsloth-bnb-4bit
导语:Qwen3-VL-8B-Thinking模… · 2026/11/4 15:56:52
从零开始学AI22——集成学习 集成学习核心思想是“三个臭皮匠,顶个诸葛亮”,通过组合多个弱模型提升整体性能。三大流派:Bagging(如随机森林)并行训练、投票降方差,适合抗噪稳定场景;Boosting(如XGBoost… · 2026/11/4 15:55:51
Sill 音频管线深度剖析:AAC-ELD 128kbps 编码、10ms 分片与逐包漂移校正的精密同步机制 【免费下载链接】sill Any window from your Mac, live on your iPhone or iPad. Touch, Apple Pencil, trackpad and keys. Free, open source, no servers. 项目地址: https://gitcode.com/gh_mirrors/sill1/sill 点击查看 免费下载 Sill 是一款免费、开源的 Mac… · 2026/11/4 15:52:50
1.65 美元通关宝可梦:Awesome Decision Models 的 50 个决策模型游戏与 Demo 完整清单 【免费下载链接】awesome-decision-models A curated list of decision models (System One / typed decision models): hosted APIs, open-weight models, runtimes, SDKs, applications, benchmarks, and papers. 项目地址: https://gitcode.com/gh_mirrors/awes/… · 2026/11/4 15:51:50
Hermes Agent容器编排监控工具:Prometheus与Grafana配置 Hermes Agent容器编排监控工具:Prometheus与Grafana配置
Hermes Agent是一款功能强大的容器编排监控工具,能够帮助开发者和运维人员轻松实现对容器环境的全面监控。本文将详细介绍如何配置Prometheus与Grafana,以实现对Hermes Agent容器编排… · 2026/11/4 15:50:49
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38
AI辅助技术栈评估:提升研发决策效率60% 1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年… · 2026/11/4 0:00:17
从生成Token到直接决策:Jev如何重塑AI Agent工程实践 1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你… · 2026/11/4 0:00:17
32G统一内存跑大模型:内存账、TPS实测与端云分流 想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统… · 2026/11/4 0:00:17
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38