dasdasd
This commit is contained in:
parent
cfd243aeda
commit
fafec7278d
@ -27,6 +27,37 @@
|
|||||||
- ✅ **声明式编程** - 只需描述"做什么",不需要"怎么做"
|
- ✅ **声明式编程** - 只需描述"做什么",不需要"怎么做"
|
||||||
- ✅ **可复用** - 一次配置,多处使用
|
- ✅ **可复用** - 一次配置,多处使用
|
||||||
- ✅ **易维护** - 修改配置即可调整流程
|
- ✅ **易维护** - 修改配置即可调整流程
|
||||||
|
- 🚀 **人类行为模拟** - 框架自动添加延迟,模拟真实用户操作
|
||||||
|
- 🎯 **智能字段推断** - 简化配置,自动查找表单字段
|
||||||
|
|
||||||
|
### 🆕 核心特性
|
||||||
|
|
||||||
|
#### 1. 人类行为延迟(自动)
|
||||||
|
框架天然支持人类行为模拟,**无需手动配置延迟**:
|
||||||
|
- 📖 页面加载后自动阅读 1-3 秒
|
||||||
|
- 💭 表单填写后自动思考 0.5-1.5 秒
|
||||||
|
- ⏸️ 字段间自动停顿 200-500ms
|
||||||
|
- 🖱️ 点击前后自动延迟 200-500ms
|
||||||
|
- 📜 滚动后自动停顿查看内容
|
||||||
|
|
||||||
|
#### 2. 智能字段推断
|
||||||
|
支持超简化配置,自动推断选择器:
|
||||||
|
```yaml
|
||||||
|
# ❌ 旧方式:需要详细配置
|
||||||
|
fields:
|
||||||
|
email:
|
||||||
|
find:
|
||||||
|
- css: '#email'
|
||||||
|
- name: 'email'
|
||||||
|
value: "user@example.com"
|
||||||
|
|
||||||
|
# ✅ 新方式:一行搞定
|
||||||
|
fields:
|
||||||
|
email: "user@example.com" # 自动查找 #email, name="email" 等
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 配置简化
|
||||||
|
从 344 行减少到 260 行,**减少 24% 配置量**,提高可读性和维护性。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -42,29 +73,25 @@ site:
|
|||||||
name: MySite
|
name: MySite
|
||||||
url: https://mysite.com/register
|
url: https://mysite.com/register
|
||||||
|
|
||||||
# 工作流定义
|
# 工作流定义(使用简化配置)
|
||||||
workflow:
|
workflow:
|
||||||
- action: navigate
|
- action: navigate
|
||||||
name: "打开注册页面"
|
name: "打开注册页面"
|
||||||
url: "https://mysite.com/register"
|
url: "https://mysite.com/register"
|
||||||
|
# 框架会自动:加载页面 → 阅读1-3秒
|
||||||
|
|
||||||
- action: fillForm
|
- action: fillForm
|
||||||
name: "填写注册信息"
|
name: "填写注册信息"
|
||||||
fields:
|
fields:
|
||||||
username:
|
username: "{{account.username}}" # 自动推断选择器
|
||||||
find:
|
email: "{{account.email}}"
|
||||||
- css: '#username'
|
# 框架会自动:字段间停顿 → 填写后思考
|
||||||
value: "{{account.username}}"
|
|
||||||
|
|
||||||
email:
|
|
||||||
find:
|
|
||||||
- css: '#email'
|
|
||||||
value: "{{account.email}}"
|
|
||||||
|
|
||||||
- action: click
|
- action: click
|
||||||
name: "提交注册"
|
name: "提交注册"
|
||||||
selector:
|
selector:
|
||||||
- css: 'button[type="submit"]'
|
- text: "Sign up" # 支持按文本查找
|
||||||
|
# 框架会自动:点击前准备 → 点击 → 点击后等待
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 创建站点适配器
|
### 2. 创建站点适配器
|
||||||
@ -186,72 +213,79 @@ errorHandling:
|
|||||||
|
|
||||||
### fillForm - 表单填写
|
### fillForm - 表单填写
|
||||||
|
|
||||||
**用途**:填写表单字段
|
**用途**:填写表单字段,支持智能字段推断和人类行为模拟
|
||||||
|
|
||||||
**必需参数**:
|
**必需参数**:
|
||||||
- `fields` (object): 字段配置对象,键为字段名
|
- `fields` (object): 字段配置对象,键为字段名
|
||||||
|
|
||||||
**可选参数**:
|
**可选参数**:
|
||||||
- `name` (string): 步骤名称
|
- `name` (string): 步骤名称
|
||||||
- `humanLike` (boolean): 是否模拟人类输入,默认 `true`
|
- `humanLike` (boolean): ⚠️ 已废弃,框架默认使用人类行为
|
||||||
|
- `clearTimes` (number): 清空字段的按键次数,默认 25
|
||||||
|
|
||||||
**字段配置**:
|
**字段配置格式**:
|
||||||
- `find` (array): 选择器数组,按顺序尝试
|
|
||||||
- `css` (string): CSS 选择器
|
#### 1. 简化配置(推荐)✨
|
||||||
- `xpath` (string): XPath 选择器
|
```yaml
|
||||||
- `text` (string): 按文本查找
|
fields:
|
||||||
- `name` (string): 按 name 属性查找
|
fieldName: "value" # 自动推断选择器
|
||||||
- `value` (string): 字段值,支持变量替换
|
```
|
||||||
- `clear` (boolean): 填写前是否清空,默认 `true`
|
框架会自动查找:
|
||||||
|
- `#fieldName`
|
||||||
|
- `[name="fieldName"]`
|
||||||
|
- `input[name="fieldName"]`
|
||||||
|
- `select[name="fieldName"]`
|
||||||
|
- `textarea[name="fieldName"]`
|
||||||
|
|
||||||
|
#### 2. 完整配置
|
||||||
|
```yaml
|
||||||
|
fields:
|
||||||
|
fieldName:
|
||||||
|
find:
|
||||||
|
- css: '#custom-id'
|
||||||
|
- name: 'custom-name'
|
||||||
|
value: "{{variable}}"
|
||||||
|
type: "input" # 或 "select"
|
||||||
|
```
|
||||||
|
|
||||||
|
**自动行为**:
|
||||||
|
- 🔍 自动等待字段出现
|
||||||
|
- ⏸️ 字段间自动停顿 200-500ms
|
||||||
|
- 💭 填写后自动思考 500-1500ms
|
||||||
|
- ⌨️ 模拟人类打字速度
|
||||||
|
|
||||||
**示例**:
|
**示例**:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# 基本表单填写
|
# ✅ 推荐:超简化配置
|
||||||
- action: fillForm
|
- action: fillForm
|
||||||
name: "填写注册信息"
|
name: "填写注册信息"
|
||||||
fields:
|
fields:
|
||||||
username:
|
username: "{{account.username}}"
|
||||||
find:
|
email: "{{account.email}}"
|
||||||
- css: '#username'
|
password: "{{account.password}}"
|
||||||
value: "{{account.username}}"
|
|
||||||
|
|
||||||
email:
|
# ✅ 混合配置(部分字段需要特殊处理)
|
||||||
find:
|
|
||||||
- css: 'input[name="email"]'
|
|
||||||
- css: '#email'
|
|
||||||
value: "{{account.email}}"
|
|
||||||
|
|
||||||
# 模拟人类输入
|
|
||||||
- action: fillForm
|
- action: fillForm
|
||||||
name: "填写个人信息"
|
name: "填写支付信息"
|
||||||
humanLike: true
|
|
||||||
fields:
|
fields:
|
||||||
firstName:
|
cardNumber: "{{card.number}}" # 简化
|
||||||
|
cardExpiry: "{{card.expiry}}" # 简化
|
||||||
|
billingCountry: # 完整配置(下拉框)
|
||||||
find:
|
find:
|
||||||
- css: '#firstName'
|
- css: '#billingCountry'
|
||||||
value: "{{account.firstName}}"
|
value: "US"
|
||||||
clear: true
|
type: "select"
|
||||||
|
|
||||||
lastName:
|
# 完整配置(需要精确控制)
|
||||||
find:
|
|
||||||
- css: '#lastName'
|
|
||||||
value: "{{account.lastName}}"
|
|
||||||
|
|
||||||
# 密码字段
|
|
||||||
- action: fillForm
|
- action: fillForm
|
||||||
name: "设置密码"
|
name: "填写复杂表单"
|
||||||
humanLike: false
|
|
||||||
fields:
|
fields:
|
||||||
password:
|
customField:
|
||||||
find:
|
find:
|
||||||
- css: 'input[type="password"]'
|
- css: 'input[data-custom="field"]'
|
||||||
value: "{{account.password}}"
|
- xpath: '//input[@placeholder="Custom"]'
|
||||||
|
value: "{{data.custom}}"
|
||||||
passwordConfirm:
|
|
||||||
find:
|
|
||||||
- css: 'input[placeholder*="Confirm"]'
|
|
||||||
value: "{{account.password}}"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -938,7 +972,38 @@ await this.executeRetryStrategy('wait', retryCount, { waitTime: 5000 });
|
|||||||
|
|
||||||
## 最佳实践
|
## 最佳实践
|
||||||
|
|
||||||
### 1. 步骤命名清晰
|
### 1. 优先使用简化配置 ✨
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# ✅ 推荐:简洁高效
|
||||||
|
- action: fillForm
|
||||||
|
fields:
|
||||||
|
email: "{{account.email}}"
|
||||||
|
password: "{{account.password}}"
|
||||||
|
|
||||||
|
# ⚠️ 仅在必要时使用完整配置
|
||||||
|
- action: fillForm
|
||||||
|
fields:
|
||||||
|
customField:
|
||||||
|
find:
|
||||||
|
- css: 'input[data-custom="field"]'
|
||||||
|
value: "{{data.value}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 删除手动延迟配置 🚀
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# ❌ 不推荐:手动配置延迟
|
||||||
|
- action: fillForm
|
||||||
|
fields: ...
|
||||||
|
waitAfter: 1000 # 框架已自动处理
|
||||||
|
|
||||||
|
# ✅ 推荐:信任框架自动延迟
|
||||||
|
- action: fillForm
|
||||||
|
fields: ... # 框架会自动添加思考延迟
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 步骤命名清晰
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# ❌ 不好
|
# ❌ 不好
|
||||||
@ -950,18 +1015,26 @@ await this.executeRetryStrategy('wait', retryCount, { waitTime: 5000 });
|
|||||||
- action: click
|
- action: click
|
||||||
name: "点击 Continue 按钮(基本信息页)"
|
name: "点击 Continue 按钮(基本信息页)"
|
||||||
selector:
|
selector:
|
||||||
- css: 'button[type="submit"]'
|
- text: 'Continue'
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 使用多个选择器备选
|
### 4. 合并连续的 fillForm
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
fields:
|
# ❌ 不推荐:拆分填写
|
||||||
email:
|
- action: fillForm
|
||||||
find:
|
fields:
|
||||||
- css: '#email' # 首选
|
cardNumber: "..."
|
||||||
- css: 'input[name="email"]' # 备选1
|
- action: fillForm
|
||||||
- xpath: '//input[@type="email"]' # 备选2
|
fields:
|
||||||
|
cardExpiry: "..."
|
||||||
|
|
||||||
|
# ✅ 推荐:合并填写(利用字段间延迟)
|
||||||
|
- action: fillForm
|
||||||
|
fields:
|
||||||
|
cardNumber: "..."
|
||||||
|
cardExpiry: "..."
|
||||||
|
cardCvc: "..."
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 验证关键步骤
|
### 3. 验证关键步骤
|
||||||
@ -1116,10 +1189,10 @@ A: 查看日志输出,每个步骤都会打印执行信息:
|
|||||||
### Q: 元素找不到怎么办?
|
### Q: 元素找不到怎么办?
|
||||||
|
|
||||||
A:
|
A:
|
||||||
1. 使用 `verifyElements` 验证元素存在
|
1. ✅ 使用简化配置,框架会自动等待元素
|
||||||
2. 增加 `waitAfter` 等待时间
|
2. 使用多个选择器备选
|
||||||
3. 使用多个选择器备选
|
3. 设置 `optional: true` 跳过
|
||||||
4. 设置 `optional: true` 跳过
|
4. ⚠️ 避免手动 `waitAfter`,框架已自动处理
|
||||||
|
|
||||||
### Q: 如何处理动态内容?
|
### Q: 如何处理动态内容?
|
||||||
|
|
||||||
@ -1180,19 +1253,29 @@ A:
|
|||||||
|
|
||||||
## 更新日志
|
## 更新日志
|
||||||
|
|
||||||
- **v1.0.0** - 基础框架
|
- **v2.0.0** 🚀 - 智能化升级(当前版本)
|
||||||
- navigate, fillForm, click, wait, custom
|
- ✨ **智能字段推断**:字段名自动推断选择器,减少 70% 配置
|
||||||
- **v1.1.0** - 增强功能
|
- 🤖 **人类行为内置**:框架天然支持人类延迟,无需手动配置
|
||||||
- retryBlock 重试块
|
- ⚡ **配置简化**:从 344 行减少到 260 行(-24%)
|
||||||
- 人类行为模拟
|
- 🔄 **自动等待**:fillForm 自动等待字段出现
|
||||||
- 点击验证
|
- 📜 **scroll action**:页面滚动支持
|
||||||
|
|
||||||
|
- **v1.3.0** - 结果验证
|
||||||
|
- verify action
|
||||||
|
- 支持 10 种条件类型
|
||||||
|
|
||||||
- **v1.2.0** - 数据提取
|
- **v1.2.0** - 数据提取
|
||||||
- extract action
|
- extract action
|
||||||
- 正则提取
|
- 正则提取
|
||||||
- 多元素提取
|
- 多元素提取
|
||||||
- **v1.3.0** - 结果验证
|
|
||||||
- verify action
|
- **v1.1.0** - 增强功能
|
||||||
- 支持 10 种条件类型
|
- retryBlock 重试块
|
||||||
|
- 人类行为模拟
|
||||||
|
- 点击验证
|
||||||
|
|
||||||
|
- **v1.0.0** - 基础框架
|
||||||
|
- navigate, fillForm, click, wait, custom
|
||||||
- 轮询检测机制
|
- 轮询检测机制
|
||||||
- 配合 retryBlock 实现智能重试
|
- 配合 retryBlock 实现智能重试
|
||||||
|
|
||||||
|
|||||||
@ -2,11 +2,13 @@ const BaseAction = require('../core/base-action');
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义动作 - 调用适配器中的自定义函数
|
* 自定义动作 - 调用适配器中的自定义函数
|
||||||
|
* 支持超时保护,防止用户代码死循环
|
||||||
*/
|
*/
|
||||||
class CustomAction extends BaseAction {
|
class CustomAction extends BaseAction {
|
||||||
async execute() {
|
async execute() {
|
||||||
const handler = this.config.handler;
|
const handler = this.config.handler;
|
||||||
const params = this.config.params || {};
|
const params = this.config.params || {};
|
||||||
|
const timeout = this.config.timeout || 300000; // 默认5分钟超时
|
||||||
|
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
throw new Error('缺少处理函数名称');
|
throw new Error('缺少处理函数名称');
|
||||||
@ -19,12 +21,29 @@ class CustomAction extends BaseAction {
|
|||||||
throw new Error(`自定义处理函数不存在: ${handler}`);
|
throw new Error(`自定义处理函数不存在: ${handler}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用自定义函数
|
// 使用 Promise.race 实现超时保护
|
||||||
const result = await this.context.adapter[handler](params);
|
const timeoutPromise = new Promise((_, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
reject(new Error(`自定义函数执行超时 (${timeout}ms): ${handler}`));
|
||||||
|
}, timeout);
|
||||||
|
});
|
||||||
|
|
||||||
this.log('debug', '✓ 自定义函数执行完成');
|
try {
|
||||||
|
// 竞速执行:函数完成 vs 超时
|
||||||
|
const result = await Promise.race([
|
||||||
|
this.context.adapter[handler](params),
|
||||||
|
timeoutPromise
|
||||||
|
]);
|
||||||
|
|
||||||
return result;
|
this.log('debug', '✓ 自定义函数执行完成');
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message.includes('执行超时')) {
|
||||||
|
this.log('error', `⚠️ ${error.message}`);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* YAML 配置验证器
|
* YAML 配置验证器
|
||||||
|
*
|
||||||
|
* 支持的配置格式:
|
||||||
|
* - 完整配置:详细指定所有选项
|
||||||
|
* - 简化配置:自动推断字段名和选择器
|
||||||
|
* - 人类行为:框架自动添加延迟,无需手动配置
|
||||||
*/
|
*/
|
||||||
class ConfigValidator {
|
class ConfigValidator {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -7,19 +12,21 @@ class ConfigValidator {
|
|||||||
this.actionSchemas = {
|
this.actionSchemas = {
|
||||||
navigate: {
|
navigate: {
|
||||||
required: ['url'],
|
required: ['url'],
|
||||||
optional: ['name', 'options', 'verifyUrl', 'verifyElements', 'waitAfter']
|
optional: ['name', 'options', 'verifyUrl', 'verifyElements', 'waitAfter', 'maxRetries', 'retryDelay', 'totalTimeout']
|
||||||
},
|
},
|
||||||
fillForm: {
|
fillForm: {
|
||||||
required: ['fields'],
|
required: ['fields'],
|
||||||
optional: ['name', 'humanLike']
|
optional: ['name', 'humanLike', 'autoRetry', 'clearTimes']
|
||||||
|
// 注意:humanLike 已废弃,框架默认使用人类行为
|
||||||
|
// fields 支持:字符串(自动推断)或对象(完整配置)
|
||||||
},
|
},
|
||||||
click: {
|
click: {
|
||||||
required: ['selector'],
|
required: ['selector'],
|
||||||
optional: ['name', 'verifyAfter', 'waitForPageChange', 'waitAfter', 'optional']
|
optional: ['name', 'verifyAfter', 'waitForPageChange', 'waitAfter', 'optional', 'timeout', 'waitForEnabled', 'humanLike', 'checkSelector']
|
||||||
},
|
},
|
||||||
wait: {
|
wait: {
|
||||||
required: ['type'],
|
required: ['type'],
|
||||||
optional: ['name', 'duration', 'find', 'timeout']
|
optional: ['name', 'duration', 'find', 'timeout', 'urlContains', 'handler']
|
||||||
},
|
},
|
||||||
extract: {
|
extract: {
|
||||||
required: ['selector', 'contextKey'],
|
required: ['selector', 'contextKey'],
|
||||||
@ -33,6 +40,10 @@ class ConfigValidator {
|
|||||||
required: ['steps'],
|
required: ['steps'],
|
||||||
optional: ['name', 'maxRetries', 'retryDelay', 'onRetryBefore', 'onRetryAfter']
|
optional: ['name', 'maxRetries', 'retryDelay', 'onRetryBefore', 'onRetryAfter']
|
||||||
},
|
},
|
||||||
|
scroll: {
|
||||||
|
required: [],
|
||||||
|
optional: ['name', 'type', 'selector', 'x', 'y', 'behavior']
|
||||||
|
},
|
||||||
custom: {
|
custom: {
|
||||||
required: ['handler'],
|
required: ['handler'],
|
||||||
optional: ['name', 'params', 'optional']
|
optional: ['name', 'params', 'optional']
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user