@isdk/web-fetcher 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.action.cn.md +469 -0
- package/README.action.md +452 -0
- package/README.cn.md +147 -0
- package/README.engine.cn.md +262 -0
- package/README.engine.md +262 -0
- package/README.md +147 -0
- package/dist/index.d.mts +1603 -0
- package/dist/index.d.ts +1603 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/docs/README.md +151 -0
- package/docs/_media/LICENSE-MIT +22 -0
- package/docs/_media/README.action.md +452 -0
- package/docs/_media/README.cn.md +147 -0
- package/docs/_media/README.engine.md +262 -0
- package/docs/classes/CheerioFetchEngine.md +1447 -0
- package/docs/classes/ClickAction.md +533 -0
- package/docs/classes/ExtractAction.md +533 -0
- package/docs/classes/FetchAction.md +444 -0
- package/docs/classes/FetchEngine.md +1230 -0
- package/docs/classes/FetchSession.md +111 -0
- package/docs/classes/FillAction.md +533 -0
- package/docs/classes/GetContentAction.md +533 -0
- package/docs/classes/GotoAction.md +537 -0
- package/docs/classes/PauseAction.md +533 -0
- package/docs/classes/PlaywrightFetchEngine.md +1437 -0
- package/docs/classes/SubmitAction.md +533 -0
- package/docs/classes/WaitForAction.md +533 -0
- package/docs/classes/WebFetcher.md +85 -0
- package/docs/enumerations/FetchActionResultStatus.md +40 -0
- package/docs/functions/fetchWeb.md +43 -0
- package/docs/globals.md +72 -0
- package/docs/interfaces/BaseFetchActionProperties.md +83 -0
- package/docs/interfaces/BaseFetchCollectorActionProperties.md +145 -0
- package/docs/interfaces/BaseFetcherProperties.md +206 -0
- package/docs/interfaces/Cookie.md +142 -0
- package/docs/interfaces/DispatchedEngineAction.md +60 -0
- package/docs/interfaces/ExtractActionProperties.md +113 -0
- package/docs/interfaces/FetchActionInContext.md +149 -0
- package/docs/interfaces/FetchActionProperties.md +125 -0
- package/docs/interfaces/FetchActionResult.md +55 -0
- package/docs/interfaces/FetchContext.md +424 -0
- package/docs/interfaces/FetchEngineContext.md +328 -0
- package/docs/interfaces/FetchMetadata.md +73 -0
- package/docs/interfaces/FetchResponse.md +105 -0
- package/docs/interfaces/FetchReturnTypeRegistry.md +57 -0
- package/docs/interfaces/FetchSite.md +320 -0
- package/docs/interfaces/FetcherOptions.md +300 -0
- package/docs/interfaces/GotoActionOptions.md +66 -0
- package/docs/interfaces/PendingEngineRequest.md +51 -0
- package/docs/interfaces/SubmitActionOptions.md +23 -0
- package/docs/interfaces/WaitForActionOptions.md +39 -0
- package/docs/type-aliases/BaseFetchActionOptions.md +11 -0
- package/docs/type-aliases/BaseFetchCollectorOptions.md +11 -0
- package/docs/type-aliases/BrowserEngine.md +11 -0
- package/docs/type-aliases/FetchActionCapabilities.md +11 -0
- package/docs/type-aliases/FetchActionCapabilityMode.md +11 -0
- package/docs/type-aliases/FetchActionOptions.md +11 -0
- package/docs/type-aliases/FetchEngineAction.md +18 -0
- package/docs/type-aliases/FetchEngineType.md +11 -0
- package/docs/type-aliases/FetchReturnType.md +11 -0
- package/docs/type-aliases/FetchReturnTypeFor.md +17 -0
- package/docs/type-aliases/OnFetchPauseCallback.md +23 -0
- package/docs/type-aliases/ResourceType.md +11 -0
- package/docs/variables/DefaultFetcherProperties.md +11 -0
- package/package.json +90 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# 📜 Action 脚本架构
|
|
2
|
+
|
|
3
|
+
[English](./README.action.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
> 本文档详细阐述了 `@isdk/web-fetcher` 中 Action 脚本的架构、设计理念和使用方式。它旨在帮助开发者维护、扩展此系统,并帮助使用者高效地构建自动化任务。
|
|
6
|
+
|
|
7
|
+
## 🎯 1. 概述
|
|
8
|
+
|
|
9
|
+
Action 脚本系统的核心目标是提供一个**声明式、引擎无关**的方式来定义和执行一系列的网页交互。
|
|
10
|
+
|
|
11
|
+
这个系统建立在两大核心概念之上:
|
|
12
|
+
|
|
13
|
+
* **⚛️ 原子 Action (Atomic Actions):** 由库内置,代表一个单一、不可再分的操作,是构成所有复杂流程的基本“原子”。例如:`goto`, `click`, `fill`。
|
|
14
|
+
* **🧩 组合式 Action (Composite Actions):** 由库的使用者创建,代表一个具有业务语义的、由多个原子 Action 组合而成的复杂操作。这是本架构设计的精髓所在,它鼓励使用者将底层操作封装成更易于理解和复用的高级“分子”。例如:`login`, `search`, `addToCart`。
|
|
15
|
+
|
|
16
|
+
通过这种方式,使用者可以用非常直观的语义来描述一个完整的业务流程,而将具体的、与引擎相关的实现细节隐藏在底层。
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🛠️ 2. 核心概念
|
|
21
|
+
|
|
22
|
+
### `FetchAction` (Action 基类)
|
|
23
|
+
|
|
24
|
+
`FetchAction` 是所有 Action 的抽象基类,它定义了一个 Action 的核心要素:
|
|
25
|
+
|
|
26
|
+
* `static id`: Action 的唯一标识符,例如 `'click'`。
|
|
27
|
+
* `static returnType`: Action 执行后返回结果的类型,例如 `'none'`, `'response'`。
|
|
28
|
+
* `static capabilities`: 声明此 Action 在不同引擎(`http`, `browser`)下的能力级别(`native`, `simulate`, `noop`)。
|
|
29
|
+
* `static register()`: 一个静态方法,用于将 Action 类注册到全局注册表中,使其可以通过 `id` 被动态创建。
|
|
30
|
+
|
|
31
|
+
### `onExecute` (核心执行逻辑)
|
|
32
|
+
|
|
33
|
+
每个 Action 子类都必须实现 `onExecute` 方法。这里是 Action 定义其行为的地方。
|
|
34
|
+
|
|
35
|
+
### `delegateToEngine` (委托辅助方法)
|
|
36
|
+
|
|
37
|
+
为了简化**原子 Action**的创建,`FetchAction` 基类提供了一个受保护的辅助方法 `delegateToEngine`。它将调用转发到活动引擎上的相应方法,并传递任何参数。这使得 Action 可以作为引擎功能的轻量级包装。
|
|
38
|
+
|
|
39
|
+
**示例:使用 `delegateToEngine` 后的 `fill` Action**
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// src/action/definitions/fill.ts
|
|
43
|
+
export class FillAction extends FetchAction {
|
|
44
|
+
// ...
|
|
45
|
+
async onExecute(context: FetchContext, options?: BaseFetchActionProperties): Promise<void> {
|
|
46
|
+
const { selector, value, ...restOptions } = options?.params || {};
|
|
47
|
+
if (!selector) throw new Error('Selector is required for fill action');
|
|
48
|
+
if (value === undefined) throw new Error('Value is required for fill action');
|
|
49
|
+
// selector, value, 和 restOptions 作为参数传递给 engine.fill()
|
|
50
|
+
await this.delegateToEngine(context, 'fill', selector, value, restOptions);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 🚀 3. 如何使用 (面向使用者)
|
|
58
|
+
|
|
59
|
+
使用者通过一个 JSON 格式的 `actions` 数组来定义一个完整的自动化流程。
|
|
60
|
+
|
|
61
|
+
### 使用原子 Action
|
|
62
|
+
|
|
63
|
+
对于简单的线性流程,可以直接使用库内置的原子 Action 列表。
|
|
64
|
+
|
|
65
|
+
**示例:在 Google 搜索 "gemini"**
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"actions": [
|
|
70
|
+
{ "id": "goto", "params": { "url": "https://www.google.com" } },
|
|
71
|
+
{ "id": "fill", "params": { "selector": "textarea[name=q]", "value": "gemini" } },
|
|
72
|
+
{ "id": "submit", "params": { "selector": "form" } }
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 内置原子 Action
|
|
78
|
+
|
|
79
|
+
本库提供了一组核心的原子 Action,用于执行常见的网页交互。
|
|
80
|
+
|
|
81
|
+
#### `goto`
|
|
82
|
+
|
|
83
|
+
导航到新的 URL。
|
|
84
|
+
|
|
85
|
+
* **`id`**: `goto`
|
|
86
|
+
* **`params`**:
|
|
87
|
+
* `url` (string): 要导航到的 URL。
|
|
88
|
+
* ...其他导航选项,如 `waitUntil`, `timeout`,这些选项会传递给引擎。
|
|
89
|
+
* **`returns`**: `response`
|
|
90
|
+
|
|
91
|
+
#### `click`
|
|
92
|
+
|
|
93
|
+
点击一个由选择器指定的元素。
|
|
94
|
+
|
|
95
|
+
* **`id`**: `click`
|
|
96
|
+
* **`params`**:
|
|
97
|
+
* `selector` (string): 用于标识要点击元素的 CSS 选择器或 XPath。
|
|
98
|
+
* **`returns`**: `none`
|
|
99
|
+
|
|
100
|
+
#### `fill`
|
|
101
|
+
|
|
102
|
+
向输入字段填充指定的值。
|
|
103
|
+
|
|
104
|
+
* **`id`**: `fill`
|
|
105
|
+
* **`params`**:
|
|
106
|
+
* `selector` (string): 输入元素的选择器。
|
|
107
|
+
* `value` (string): 要填入元素中的文本。
|
|
108
|
+
* **`returns`**: `none`
|
|
109
|
+
|
|
110
|
+
#### `submit`
|
|
111
|
+
|
|
112
|
+
提交一个表单。
|
|
113
|
+
|
|
114
|
+
* **`id`**: `submit`
|
|
115
|
+
* **`params`**:
|
|
116
|
+
* `selector` (string, optional): 表单元素的选择器。
|
|
117
|
+
* **`returns`**: `none`
|
|
118
|
+
|
|
119
|
+
#### `waitFor`
|
|
120
|
+
|
|
121
|
+
暂停执行以等待特定条件满足。
|
|
122
|
+
|
|
123
|
+
* **`id`**: `waitFor`
|
|
124
|
+
* **`params`**: 一个指定等待条件的对象 (例如 `ms`, `selector`, `networkIdle`)。
|
|
125
|
+
* **`returns`**: `none`
|
|
126
|
+
|
|
127
|
+
#### `pause`
|
|
128
|
+
|
|
129
|
+
暂停 Action 脚本的执行,以允许用户手动介入(例如,解决验证码)。
|
|
130
|
+
|
|
131
|
+
此 Action **必须**在 `fetchWeb` 的选项中提供一个 `onPause` 回调处理器。当此 Action 被触发时,它会调用 `onPause` 处理器并等待其执行完成。
|
|
132
|
+
|
|
133
|
+
* **`id`**: `pause`
|
|
134
|
+
* **`params`**:
|
|
135
|
+
* `selector` (string, optional): 如果提供,仅当匹配此选择器的元素存在时,Action 才会暂停。
|
|
136
|
+
* `attribute` (string, optional): 与 `selector` 配合使用。如果提供,仅当元素存在且拥有该指定属性时,Action 才会暂停。
|
|
137
|
+
* `message` (string, optional): 一个将传递给 `onPause` 处理器的消息,可用于向用户显示提示信息。
|
|
138
|
+
* **`returns`**: `none`
|
|
139
|
+
|
|
140
|
+
**示例:在 Google 搜索中处理 CAPTCHA**
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"actions": [
|
|
145
|
+
{ "id": "goto", "params": { "url": "https://www.google.com/search?q=gemini" } },
|
|
146
|
+
{
|
|
147
|
+
"id": "pause",
|
|
148
|
+
"params": {
|
|
149
|
+
"selector": "#recaptcha",
|
|
150
|
+
"message": "检测到 Google CAPTCHA,请在浏览器中手动解决后按回车键继续。"
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
{ "id": "waitFor", "params": { "selector": "#search" } }
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**`onPause` 处理器示例:**
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// 在调用 fetchWeb 的代码中
|
|
162
|
+
import { fetchWeb } from '@isdk/web-fetcher';
|
|
163
|
+
import readline from 'readline';
|
|
164
|
+
|
|
165
|
+
const handlePause = async ({ message }) => {
|
|
166
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
167
|
+
await new Promise(resolve => {
|
|
168
|
+
rl.question(message || '执行已暂停,请按回车键继续...', () => {
|
|
169
|
+
rl.close();
|
|
170
|
+
resolve();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
await fetchWeb({
|
|
176
|
+
// ...,
|
|
177
|
+
engine: 'browser',
|
|
178
|
+
engineOptions: { headless: false },
|
|
179
|
+
onPause: handlePause,
|
|
180
|
+
actions: [
|
|
181
|
+
// ... 你的 actions
|
|
182
|
+
]
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### `getContent`
|
|
187
|
+
|
|
188
|
+
获取当前页面状态的完整内容。
|
|
189
|
+
|
|
190
|
+
* **`id`**: `getContent`
|
|
191
|
+
* **`params`**: (无)
|
|
192
|
+
* **`returns`**: `response`
|
|
193
|
+
|
|
194
|
+
#### `extract`
|
|
195
|
+
|
|
196
|
+
使用一个强大且声明式的 Schema 从当前页面中提取结构化数据。这是进行数据采集的核心 Action。
|
|
197
|
+
|
|
198
|
+
* **`id`**: `extract`
|
|
199
|
+
* **`params`**: 一个 `ExtractSchema` 对象, 用于定义提取规则。
|
|
200
|
+
* **`returns`**: `any` (提取出的数据)
|
|
201
|
+
|
|
202
|
+
##### 提取 Schema 详解
|
|
203
|
+
|
|
204
|
+
`params` 对象本身就是一个 Schema, 用于描述您想提取的数据结构。
|
|
205
|
+
|
|
206
|
+
###### 1. 提取单个值
|
|
207
|
+
|
|
208
|
+
最基础的提取,可以指定 `selector` (CSS 选择器), `attribute` (要提取的属性名), 以及 `type` (string, number, boolean, html)。
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"id": "extract",
|
|
213
|
+
"params": {
|
|
214
|
+
"selector": "h1.main-title",
|
|
215
|
+
"type": "string"
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
> 上例将提取 class 为 `main-title` 的 `<h1>` 标签的文本内容。
|
|
221
|
+
|
|
222
|
+
###### 2. 提取对象
|
|
223
|
+
|
|
224
|
+
通过 `type: 'object'` 和 `properties` 字段来定义一个结构化对象。
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"id": "extract",
|
|
229
|
+
"params": {
|
|
230
|
+
"type": "object",
|
|
231
|
+
"selector": ".author-bio",
|
|
232
|
+
"properties": {
|
|
233
|
+
"name": { "selector": ".author-name" },
|
|
234
|
+
"email": { "selector": "a.email", "attribute": "href" }
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
###### 3. 提取数组 (便捷用法)
|
|
241
|
+
|
|
242
|
+
通过 `type: 'array'` 来提取一个列表。为了让最常见的操作更简单,我们提供了一些便捷用法。
|
|
243
|
+
|
|
244
|
+
* **提取文本数组 (默认行为)**: 当您想提取一个文本列表时,只需提供选择器,省略 `items` 即可。这是最常见的用法。
|
|
245
|
+
|
|
246
|
+
```json
|
|
247
|
+
|
|
248
|
+
{
|
|
249
|
+
|
|
250
|
+
"id": "extract",
|
|
251
|
+
|
|
252
|
+
"params": {
|
|
253
|
+
|
|
254
|
+
"type": "array",
|
|
255
|
+
|
|
256
|
+
"selector": ".tags li"
|
|
257
|
+
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
> 上例将返回一个包含所有 `<li>` 标签文本的数组, 如 `["tech", "news"]`。
|
|
265
|
+
|
|
266
|
+
* **提取属性数组 (快捷方式)**: 当您只想提取一个属性列表(例如所有链接的 `href`)时,也无需嵌套 `items`。直接在 `array` 定义中声明 `attribute` 即可。
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
|
|
270
|
+
{
|
|
271
|
+
|
|
272
|
+
"id": "extract",
|
|
273
|
+
|
|
274
|
+
"params": {
|
|
275
|
+
|
|
276
|
+
"type": "array",
|
|
277
|
+
|
|
278
|
+
"selector": ".gallery img",
|
|
279
|
+
|
|
280
|
+
"attribute": "src"
|
|
281
|
+
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
> 上例将返回一个包含所有 `<img>` 标签 `src` 属性的数组。
|
|
289
|
+
|
|
290
|
+
###### 4. 精确筛选: `has` 和 `exclude`
|
|
291
|
+
|
|
292
|
+
您可以在任何包含 `selector` 的 Schema 中使用 `has` 和 `exclude` 字段来精确控制元素的选择。
|
|
293
|
+
|
|
294
|
+
* `has`: 一个 CSS 选择器,用于确保所选元素**必须包含**匹配此选择器的后代元素。
|
|
295
|
+
* `exclude`: 一个 CSS 选择器,用于从结果中**排除**匹配此选择器的元素。
|
|
296
|
+
|
|
297
|
+
**完整示例: 提取包含图片且未被标记为"草稿"的文章链接**
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"actions": [
|
|
302
|
+
{ "id": "goto", "params": { "url": "https://example.com/articles" } },
|
|
303
|
+
{
|
|
304
|
+
"id": "extract",
|
|
305
|
+
"params": {
|
|
306
|
+
"type": "array",
|
|
307
|
+
"selector": "div.article-card",
|
|
308
|
+
"has": "img.cover-image",
|
|
309
|
+
"exclude": ".draft",
|
|
310
|
+
"items": {
|
|
311
|
+
"selector": "a.title-link",
|
|
312
|
+
"attribute": "href"
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
]
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
> 上述 `extract` Action 会:
|
|
321
|
+
>
|
|
322
|
+
> 1. 找到所有 `div.article-card` 元素。
|
|
323
|
+
> 2. 筛选出其中必须包含 `<img class="cover-image">` 的元素。
|
|
324
|
+
> 3. 再从结果中排除掉自身带有 `.draft` 类的元素。
|
|
325
|
+
> 4. 对于最终剩下的每个 `div.article-card`, 找到其后代 `a.title-link` 并提取 `href` 属性。
|
|
326
|
+
|
|
327
|
+
### 通过“组合”构建高级语义 Action
|
|
328
|
+
|
|
329
|
+
这是推荐给**使用者**的最佳实践,用于封装和复用业务流程。
|
|
330
|
+
|
|
331
|
+
**场景:创建一个可复用的 `LoginAction`**
|
|
332
|
+
|
|
333
|
+
1. **在您的项目中定义 `LoginAction.ts`**
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
import { FetchContext, FetchAction, BaseFetchActionOptions } from '@isdk/web-fetcher';
|
|
337
|
+
|
|
338
|
+
export class LoginAction extends FetchAction {
|
|
339
|
+
static override id = 'login';
|
|
340
|
+
static override capabilities = { http: 'simulate' as const, browser: 'native' as const };
|
|
341
|
+
|
|
342
|
+
async onExecute(context: FetchContext, options?: BaseFetchActionOptions): Promise<void> {
|
|
343
|
+
const { username, password, userSelector, passSelector, submitSelector } = options?.params || {};
|
|
344
|
+
if (!username || !password || !userSelector || !passSelector || !submitSelector) {
|
|
345
|
+
throw new Error('Username, password, and all selectors are required for login action');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const engine = context.internal.engine;
|
|
349
|
+
if (!engine) throw new Error('No engine available');
|
|
350
|
+
|
|
351
|
+
// 编排原子能力,形成一个完整的业务流程
|
|
352
|
+
await engine.fill({ selector: userSelector, value: username });
|
|
353
|
+
await engine.fill({ selector: passSelector, value: password });
|
|
354
|
+
await engine.click({ selector: submitSelector });
|
|
355
|
+
await engine.waitFor({ networkIdle: true });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
2. **在您的应用启动时,注册这个自定义 Action**
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
import { FetchAction } from '@isdk/web-fetcher';
|
|
364
|
+
import { LoginAction } from './path/to/LoginAction';
|
|
365
|
+
|
|
366
|
+
FetchAction.register(LoginAction);
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
3. **在脚本中使用您的 `LoginAction`**
|
|
370
|
+
|
|
371
|
+
现在,您的 Action 脚本变得非常简洁且富有语义:
|
|
372
|
+
|
|
373
|
+
```json
|
|
374
|
+
{
|
|
375
|
+
"actions": [
|
|
376
|
+
{
|
|
377
|
+
"id": "login",
|
|
378
|
+
"params": {
|
|
379
|
+
"username": "testuser",
|
|
380
|
+
"password": "password123",
|
|
381
|
+
"userSelector": "#username",
|
|
382
|
+
"passSelector": "#password",
|
|
383
|
+
"submitSelector": "button[type=submit]"
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## 🧲 4. 高级功能:采集器 (Collectors)
|
|
393
|
+
|
|
394
|
+
采集器(Collector)是一种强大的机制,它允许一个**主 Action**在执行期间,运行一个或多个**子 Action**来并行地、事件驱动地收集数据。
|
|
395
|
+
|
|
396
|
+
### 核心概念
|
|
397
|
+
|
|
398
|
+
采集器被定义在主 Action 的 `collectors` 数组中。它的执行由事件驱动:
|
|
399
|
+
|
|
400
|
+
* `activateOn`: 激活采集器的事件。
|
|
401
|
+
* `collectOn`: 触发采集器 `onExecute` 核心逻辑的事件。
|
|
402
|
+
* `deactivateOn`: 停用采集器的事件。
|
|
403
|
+
* `storeAs`: 用于在 `context.outputs` 中存储采集结果的键名。
|
|
404
|
+
|
|
405
|
+
> **ℹ️ 特殊规则**:如果一个采集器没有配置任何 `On` 事件,它将在主 Action 的 `end` 事件触发时,执行一次 `onExecute`。
|
|
406
|
+
|
|
407
|
+
### 采集器适用场景
|
|
408
|
+
|
|
409
|
+
> **⚠️ 重要提示**: 虽然机制上任何 Action 都可以被用作采集器,但只有那些**以返回数据为目的的 Action** (例如 `getContent`, `extract`) 才具有实际意义。使用像 `click` 或 `fill` 这样不返回任何内容的 Action 作为采集器是毫无意义的。
|
|
410
|
+
|
|
411
|
+
### 使用示例
|
|
412
|
+
|
|
413
|
+
**场景**:访问一个博客页面,并在页面加载完成后,采集所有的超链接(`<a>` 标签的 `href`)。
|
|
414
|
+
|
|
415
|
+
```json
|
|
416
|
+
{
|
|
417
|
+
"actions": [
|
|
418
|
+
{
|
|
419
|
+
"id": "goto",
|
|
420
|
+
"params": { "url": "https://example.com/blog/my-post" },
|
|
421
|
+
"collectors": [
|
|
422
|
+
{
|
|
423
|
+
"id": "extract",
|
|
424
|
+
"name": "linkCollector",
|
|
425
|
+
"params": {
|
|
426
|
+
"type": "array",
|
|
427
|
+
"selector": "a",
|
|
428
|
+
"attribute": "href"
|
|
429
|
+
},
|
|
430
|
+
"storeAs": "allLinks"
|
|
431
|
+
}
|
|
432
|
+
]
|
|
433
|
+
}
|
|
434
|
+
]
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**执行流程**:
|
|
439
|
+
|
|
440
|
+
1. 主 Action `goto` 开始执行。
|
|
441
|
+
2. `linkCollector` 被初始化。
|
|
442
|
+
3. 由于没有触发器,它默认等待 `goto` 动作完成。
|
|
443
|
+
4. `goto` 成功加载页面并触发其内部的 `action:goto.end` 事件。
|
|
444
|
+
5. `linkCollector` 监听到此事件并执行,提取所有 `<a>` 标签的 `href`。
|
|
445
|
+
6. 结果被推入 `context.outputs.allLinks` 数组。
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## 🧑💻 5. 如何扩展 (面向开发者)
|
|
450
|
+
|
|
451
|
+
作为库的开发者,您的主要职责是丰富**原子 Action**生态。
|
|
452
|
+
|
|
453
|
+
### 添加新的“原子 Action”
|
|
454
|
+
|
|
455
|
+
1. **在引擎中定义能力:** 在 `src/engine/base.ts` 的 `FetchEngine` 中添加新的抽象方法,并在具体的引擎(`Cheerio`, `Playwright`)中实现它。
|
|
456
|
+
2. **创建 Action 类:** 创建一个新的 Action 类文件,例如 `src/action/definitions/MyNewAction.ts`。
|
|
457
|
+
3. **实现 `onExecute`:** 对于简单情况,使用 `delegateToEngine` 辅助方法。
|
|
458
|
+
4. **注册 Action:** 在你的新文件中调用 `FetchAction.register(MyNewAction)`。
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## 🔄 6. Action 生命周期
|
|
463
|
+
|
|
464
|
+
`FetchAction` 基类提供了一套生命周期钩子,允许在 Action 执行的核心逻辑前后注入自定义行为。
|
|
465
|
+
|
|
466
|
+
* `protected onBeforeExec?()`: 在 `onExecute` 执行前调用。
|
|
467
|
+
* `protected onAfterExec?()`: 在 `onExecute` 执行后调用。
|
|
468
|
+
|
|
469
|
+
对于需要管理复杂状态或资源的 Action,可以实现这些钩子。通常,对于组合式 Action,直接在 `onExecute` 中编写逻辑已经足够。
|