بنقرة واحدة
web-form-fill
网络表单填报技能 — 从信息搜集到浏览器填报的完整工作流,确保框架受控组件正确写入、页面切换前暂存、最终提交由用户确认。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
网络表单填报技能 — 从信息搜集到浏览器填报的完整工作流,确保框架受控组件正确写入、页面切换前暂存、最终提交由用户确认。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
软件著作权登记全流程:从代码仓/目录生成程序鉴别材料(源程序文档)、 软件操作手册、申请填报信息 Markdown,并可辅助在线填报。 当用户需要申请软件著作权、生成软著材料时触发。
维护 business-developer 的 SQLite 追踪数据库,记录已探索的创作者(模式一)和已互动的帖子(模式二),避免重复追踪和重复互动。
通过自媒体平台搜索内容,在评论区以留言/回复/私信等方式拓展潜在客户或进行品牌宣传。用于 HEARTBEAT 定时任务。
维护 business-developer 的 SQLite 情报采集数据库,记录已采集的信息内容,避免重复采集,支持按日查询已采集情报。
定时监控特定信源(自媒体账号/网页),按预设标准提取商业情报,生成简报或报告。用于 cron 定时任务。
通过自媒体平台按搜集策略探索潜在客户——策略 A 分析帖子发布者画像,策略 B 从评论区挖掘潜客。用于 HEARTBEAT 定时任务。
| name | web-form-fill |
| description | 网络表单填报技能 — 从信息搜集到浏览器填报的完整工作流,确保框架受控组件正确写入、页面切换前暂存、最终提交由用户确认。 |
| metadata | {"openclaw":{"emoji":"📝"}} |
面向在线表单填报场景:申报系统、比赛报名、补贴申请、信息登记等。核心原则:先看后填、模拟真人、切页必存、提交由人。
依赖技能:browser-guide(浏览器操作最佳实践)
打开浏览器后,先完整浏览整个表单,不要急着填。
form-fields-{项目简称}.md),保持与表单相同的页面/段落结构markdown 文件结构示例:
# XX大赛申报 — 字段清单
## 概要
- 项目名称 [必填] [text] [限50字]
- 行业分类 [必填] [级联下拉] 一级→二级
- 科技创新领域 [必填] [下拉单选]
- 参赛渠道 [必填] [下拉多选]
- 以投代评 [必填] [radio: 是/否]
- 服务需求 [多选] 最多3项
- 期望融资金额 [必填] [number] 万元
- 股权释放比例 [必填] [number] %
## 参赛项目情况
- 项目名称 [必填] [text] (概要中已填,此处联动)
- 前沿技术热点归属 [必填] [text] 最多5个关键词,逗号分隔
- 项目主要技术、产品及服务 [必填] [富文本] 2000字以内,可插入图片
- 项目产品市场分析及竞争优势 [必填] [富文本] 2000字以内
- 商业模式 [必填] [富文本] 1000字以内
## 企业基本信息表
...
## 附件
- 营业执照 [必填] [PDF/图片]
- ...
回到 markdown 文件,逐项填写准备好的内容:
问用户的典型场景:
信息齐全后,打开浏览器逐页填报。
核心原则:每个字段都要「尝试 → 验证 → 失败则降级重试」。不同网站拦截程度不同,没有一种方法能通吃所有情况。
对每个文本字段,按以下顺序逐级尝试,每级尝试后立即验证,验证通过则停止,失败则清空重来:
| 级别 | 方法 | 原理 | 适用场景 | 失效场景 |
|---|---|---|---|---|
| A | CDP Input.insertText | 走浏览器真实输入管线,一次性插入整段文本 | 大多数原生 input 和简单受控组件:Vue/React/Svelte v-model、原生 input | 有输入 mask 的字段、拦截 insertText 的站点、Slate/wangEditor 等 contenteditable 编辑器可能不吃 |
| S | JS dispatch beforeinput 事件 | 从 JS 层手动构建 InputEvent('beforeinput', {inputType:'insertText'}) 并 dispatch,走 Slate 的原生事件管道 | Slate/wangEditor/Tiptap 等 contenteditable 编辑器 — 这些编辑器监听 beforeinput 而非 input,CDP insertText 不一定能触发 | 非 Slate 的 contenteditable、原生 input |
| B | CDP 逐字 Input.dispatchKeyEvent | 模拟逐键按下,每个字符发 keyDown → char → keyUp | 输入 mask 字段、只监听 keydown/keyup 的组件、insertText 被拦截的站点 | 监听 composition 事件的中文输入场景、非常长的文本(太慢) |
| C | JS Clipboard 粘贴 | document.execCommand('insertText', false, text) | 部分 contenteditable 编辑器、允许粘贴的输入框 | 禁止粘贴的站点、已废弃 execCommand 的浏览器、Slate 不感知 execCommand 的 DOM 变更 |
| D | JS native setter + dispatchEvent | 用 Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set 绕过框架拦截设值,再手动 dispatchEvent('input') + dispatchEvent('change') | 简单 Vue/React v-model 绑定、CDP 完全不可用时 | Slate/wangEditor 等非 input 编辑器(无 .value 属性)、依赖逐字验证的组件 |
⚠️ Slate/wangEditor 的致命坑:
Slate 系编辑器(wangEditor、Tiptap 等)与普通受控组件有本质区别,必须注意:
beforeinput,不监听 input — 普通 Vue/React 组件靠 input 事件同步状态,Slate 靠 beforeinput。所以级别 D 的 dispatchEvent('input') 对 Slate 完全无效execCommand 改的是 DOM,Slate 不知道 — document.execCommand('insertText') 直接修改 DOM,但 Slate 的内部数据模型不变,下次 Slate 重渲染会覆盖掉beforeinput 后 DOM 更新有延迟 — beforeinput 被 Slate 接管后,文本已写入 Slate 数据模型,但 DOM 渲染要等微任务队列跑完。验证时不能立即读 textContent,必须延迟 50ms+ 再验证beforeinput({inputType: 'deleteContentBackward'})如何判断当前字段是否为 Slate 编辑器:
[data-slate-editor]、[data-slate-node].w-e-text-container(wangEditor)每级尝试的完整流程:
对每个字段:
1. scrollIntoView 确保在视口内
2. 点击聚焦(对 input:点击输入框;对 contenteditable:点击编辑区域)
3. 清空已有内容(见下方清空方式)
4. 判断字段类型:
- 如果是 Slate/contenteditable 编辑器 → 从级别 S 开始
- 如果是普通 input/textarea → 从级别 A 开始
5. 按级别依次尝试:
a. 用当前方法写入文本
b. 验证写入是否生效(见下方验证规则)
c. 如果验证通过 → 本字段完成,进入下一个字段
d. 如果验证失败 → 清空内容,降级到下一级别重试
6. 如果所有级别都失败 → 记录该字段为"无法自动填写",最后告知用户手动填写
各级别操作细节:
级别 A — CDP Input.insertText(普通 input/textarea 首选):
1. 聚焦(点击元素)
2. 清空已有内容
3. CDP Input.insertText: "要填入的文本"
4. 验证
级别 S — JS dispatch beforeinput 事件(Slate/contenteditable 编辑器首选):
1. 聚焦(点击编辑器内容区域,确认 activeElement 是编辑器或其子节点)
2. 清空已有内容(用 beforeinput deleteContentBackward,见下方清空方式)
3. 对文本中每个字符 ch:
Runtime.evaluate:
const be = new InputEvent('beforeinput', {
inputType: 'insertText',
data: ch,
bubbles: true,
cancelable: true,
composed: true
});
editor.dispatchEvent(be);
短暂等待(10-20ms)
4. 延迟 50ms 后验证(Slate DOM 更新有延迟)
级别 B — 逐字 dispatchKeyEvent:
1. 聚焦(点击元素)
2. 清空已有内容
3. 对文本中每个字符 ch:
- Input.dispatchKeyEvent: type=keyDown, key=ch, code=KeyX
- Input.dispatchKeyEvent: type=char, text=ch
- Input.dispatchKeyEvent: type=keyUp, key=ch, code=KeyX
- 短暂等待(20-50ms,模拟人类打字节奏)
4. 验证
注意:中文等非 ASCII 字符,用 type=char 事件 + text 参数传递
级别 C — JS Clipboard 粘贴:
1. 聚焦(点击元素)
2. 清空已有内容
3. Runtime.evaluate: document.execCommand('insertText', false, '要填入的文本')
4. 延迟 50ms 后验证
注意:对 Slate 编辑器此方法大概率无效(execCommand 改 DOM 不改 Slate 模型),但仍可一试
级别 D — native setter + dispatchEvent(仅适用于 /,不适用于 contenteditable):
1. 聚焦(点击元素)
2. 清空已有内容
3. Runtime.evaluate:
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
setter.call(el, '要填入的文本');
el.dispatchEvent(new Event('input', {bubbles: true}));
el.dispatchEvent(new Event('change', {bubbles: true}));
4. 验证
清空已有内容:
对 普通 input/textarea:
1. triple-click 全选(Input.dispatchMouseEvent clickCount=3)
2. Backspace 删除
3. 如果 triple-click 无效,改用 Ctrl+A → Delete
对 contenteditable / Slate 编辑器:
1. 聚焦编辑器
2. 全选:Ctrl+A(Input.dispatchKeyEvent keyDown key=a modifiers=2)
3. 删除:dispatch beforeinput 事件
Runtime.evaluate:
editor.dispatchEvent(new InputEvent('beforeinput', {
inputType: 'deleteContentBackward',
bubbles: true,
cancelable: true,
composed: true
}));
4. 如果 beforeinput 删除无效,回退到 Backspace 键事件
5. 延迟 50ms 后验证内容为空
禁止的方式:
innerHTML — Slate/Vue 不会感知,placeholder 不消失,暂存后内容丢失textContent — 同上el.value = "xxx" 不发事件 — 框架完全无感知dispatchEvent('input') — Slate 不监听 input 事件,只监听 beforeinputexecCommand — 改 DOM 不改 Slate 数据模型,下次渲染被覆盖逐级尝试:
| 级别 | 方法 |
|---|---|
| A | 点击 select 打开下拉面板 → 点击目标选项 |
| B | 点击 select → 键盘方向键选择 → Enter 确认 |
| C | native setter 设值 + dispatchEvent('change') |
自定义下拉(非原生 <select>,如 Element Plus el-select、Ant Design Select):
Input.dispatchMouseEvent 点击目标选项逐级尝试:
| 级别 | 方法 |
|---|---|
| A | 点击日期输入框 → 在弹出的日历面板中逐年/月/日点击 |
| B | 点击日期输入框聚焦 → 用级别 A/B 文本输入方法输入日期字符串 |
| C | native setter 设值 + dispatchEvent |
<input type="file"> 或上传按钮DOM.setFileInputFiles 设置文件路径| 控件类型 | 验证方式 | 通过条件 | 注意 |
|---|---|---|---|
| 普通 input | 检查 el.value 和 placeholder 可见性 | el.value 包含预期内容 且 placeholder 不可见 | 可立即验证 |
| contenteditable / Slate 编辑器 | 检查 editor.textContent.trim() 和 placeholder 元素 | textContent 包含预期内容 且 placeholder 元素 display: none | 必须延迟 50ms 再验证 — Slate 处理 beforeinput 后 DOM 更新有延迟 |
| 下拉 select | 检查选中项文本 | 选中项文本与预期一致 | 可立即验证 |
| radio/checkbox | 检查 el.checked | 选中状态符合预期 | 可立即验证 |
contenteditable 验证的具体实现:
// 等待 Slate DOM 更新后验证
await new Promise(r => setTimeout(r, 50));
const text = editor.textContent.trim();
const ph = editor.closest('.w-e-text-container')
?.querySelector('.w-e-text-placeholder');
const phHidden = !ph || getComputedStyle(ph).display === 'none';
const passed = text.length > 0 && phHidden;
验证失败的处理:
切换页面/栏位前,必须执行:
如果当前页没有暂存按钮:
所有页面填写完成后:
原因:直接操作 DOM 绕过了框架响应式系统(Vue/React/Slate),框架内部状态仍为空。
解决:按 3-B 节的逐级尝试策略填写。对普通 input 优先 CDP Input.insertText;对 Slate 编辑器优先 dispatch beforeinput 事件。每级尝试后验证,失败则降级。
原因:同上 — 框架内部状态为空,暂存提交的是空值。
验证方法:暂存后,导航到其他页面再回来,检查字段是否仍有值。
原因:Slate 处理 beforeinput 后,文本已写入数据模型但 DOM 还没渲染完。立即读 textContent 会读到空值。
解决:验证前延迟 50ms+,等 Slate 微任务队列跑完再读 textContent。
对策:
[contenteditable="true"]),而非工具栏document.activeElement 是编辑器或其子节点[data-slate-editor]),是则从级别 S(beforeinput)开始尝试对策:
对策:
browser-guide 的 CAPTCHA 处理流程操作1. Runtime.evaluate: el.scrollIntoView({block:'center'})
2. Runtime.evaluate: el.getBoundingClientRect() → {x, y, width, height}
3. Input.dispatchMouseEvent: mousePressed at (x+width/2, y+height/2)
4. Input.dispatchMouseEvent: mouseReleased
1. 聚焦元素(见上)
2. Input.dispatchMouseEvent: mousePressed clickCount=3 (triple-click 全选)
3. Input.dispatchMouseEvent: mouseReleased clickCount=3
4. Input.dispatchKeyEvent: keyDown Backspace
5. Input.dispatchKeyEvent: keyUp Backspace
6. 验证: el.value 为空
如果 triple-click 无效,改用 Ctrl+A:
2. Input.dispatchKeyEvent: keyDown key=a modifiers=2 (Ctrl)
3. Input.dispatchKeyEvent: keyUp key=a modifiers=2
1. 聚焦编辑器
2. Input.dispatchKeyEvent: keyDown key=a modifiers=2 (Ctrl+A 全选)
3. Input.dispatchKeyEvent: keyUp key=a modifiers=2
4. Runtime.evaluate:
editor.dispatchEvent(new InputEvent('beforeinput', {
inputType: 'deleteContentBackward',
bubbles: true, cancelable: true, composed: true
}));
5. 等待 50ms
6. 验证: editor.textContent.trim() 为空
如果 beforeinput 删除无效,回退到 Backspace 键事件。
1. 聚焦 + 清空
2. Input.insertText: "要填入的内容"
3. 验证: el.value 非空 且 placeholder 不可见
1. 聚焦 + 清空(用 beforeinput 方式清空)
2. 对每个字符 ch:
Runtime.evaluate:
editor.dispatchEvent(new InputEvent('beforeinput', {
inputType: 'insertText', data: ch,
bubbles: true, cancelable: true, composed: true
}));
等待 10-20ms
3. 等待 50ms(Slate DOM 更新延迟)
4. 验证: editor.textContent.trim() 非空 且 placeholder display=none
1. 聚焦 + 清空
2. 对每个字符 ch:
Input.dispatchKeyEvent: type=keyDown, key=ch
Input.dispatchKeyEvent: type=char, text=ch
Input.dispatchKeyEvent: type=keyUp, key=ch
等待 20-50ms
3. 验证
1. 聚焦 + 清空
2. Runtime.evaluate: document.execCommand('insertText', false, '要填入的内容')
3. 等待 50ms(contenteditable DOM 更新延迟)
4. 验证
1. 聚焦 + 清空
2. Runtime.evaluate:
const s = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
s.call(el, '要填入的内容');
el.dispatchEvent(new Event('input', {bubbles: true}));
el.dispatchEvent(new Event('change', {bubbles: true}));
3. 验证