# Xrk Crawl

> 当你需要开发/排查 HTTP 抓取、SSRF、Playwright 受控浏览器、本地字体增强截图，或判断 web_fetch 与 browser 工作流如何选型时使用。

- Skill: `sunflowermm/xrk-crawl` (Agent Skill)
- Install (CLI): `npx skillmds@latest add sunflowermm/xrk-crawl`
- Raw SKILL.md: https://api.skillmd.com/api/skills/sunflowermm/xrk-crawl/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: sunflowermm (https://skillmd.com/u/sunflowermm)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/sunflowermm/xrk-crawl

---


## 统一入口（业务优先）

`src/infrastructure/crawl/index.js` — 插件、workflow、HTTP **只从这里 import**（`#infrastructure/crawl/index.js`）。

```javascript
import {
  fetchWithPolicy,
  runWebFetch,
  buildWebFetchRuntime,
  assertUrlSafeForFetch,
  PlaywrightAgentSession,
  launchOptionsFromBrowserRuntime,
  toPlaywrightAgentLaunchOptions,
  createLocalFontScreenshotHelper,
  DEFAULT_DEVICE_SCALE_FACTOR,
  DOM_TWEAK_LABEL_COLON_HALF,
} from '#infrastructure/crawl/index.js'
```

`fetchWithPolicy` 实现在 `#utils/fetch-with-retry.js`，由 crawl 门面 re-export。

## 能力分层（何时用谁）

| 场景 | 能力 | 实现文件（均在 `src/infrastructure/crawl/`） |
|------|------|------------------------------|
| 简单 API / 无需 JS 渲染 | `fetchWithPolicy` | `#utils/fetch-with-retry.js` |
| 正文提取、Readability、Firecrawl | `runWebFetch` | `web-fetch-executor.js` |
| 开放域检索 | `runWebSearch` | `web-search-executor.js` + `web-search-registry.js` |
| 零配置免费检索 | `runParallelFreeSearch` | `web-search-parallel-free.js` + `web-search-mcp-client.js` |
| 浏览器运行时 | `buildBrowserRuntime` | `crawl-config.js`（ai-workflow.crawl + renderer.playwright） |
| runtime → launch | `toPlaywrightAgentLaunchOptions` / `launchOptionsFromBrowserRuntime` | `crawl-config.js` |
| MCP `web_fetch` / `web_search` | `workflow/web.js` | 内部用 crawl |
| JS 渲染、交互、截图 | `PlaywrightAgentSession` | `playwright-session.js` |
| MCP 受控浏览器 | `workflow/browser.js` | `browser_*` 工具 |
| 截图字体/样式与线上一致 | `createLocalFontScreenshotHelper` | `page-screenshot-enhance.js` |

**选型**：HTTP 能拿正文 → `runWebFetch`；要渲染或 PNG → Playwright。

## Playwright 启动（必读）

业务/插件**禁止**手抄 `executablePath` / `launchArgs` / 只挑部分字段。统一：

```javascript
// 推荐：配置链 + 业务覆盖（viewport / DPR）
await PlaywrightAgentSession.using(
  launchOptionsFromBrowserRuntime({ deviceScaleFactor: 3, viewport: { width: 520, height: 960 } }),
  async (session) => { /* ... */ }
)

// 已有 runtime 对象时
const rt = buildBrowserRuntime()
await PlaywrightAgentSession.launch(toPlaywrightAgentLaunchOptions(rt))
```

映射会带上：`navigationTimeoutMs`、`ssrfPolicy`、`closeTimeoutMs`、`pageCrashRetries`、`opTimeoutMs`、系统浏览器路径、`launchArgs`。

**稳定性（底层默认，插件勿再造）**：

| 能力 | 说明 |
|------|------|
| `using(..., { crashRetries: 1 })` | 整轮 Target/Page crashed → 换新浏览器再跑 |
| `goto` / `gotoAndCapture` | 页崩溃 → `recreatePage` 后重试（后者含重新导航） |
| `session.close()` | `softClosePlaywrightTree`（`AbortSignal.timeout` 竞速），超时不卡死 |
| `waitUntil` | 经 `normalizePlaywrightWaitUntil`：`load`（默认）/`domcontentloaded`/`networkidle`/`commit`；非法值回落 `load`。官方 **DISCOURAGED** `networkidle`，优先 `load` 或交互后断言就绪 |

**反模式**：同一重型（高 DPR + 字体）会话连开多档位 `goto`；在插件里硬编码 Chrome 路径 / soft-close / 崩溃正则；默认依赖 `networkidle`。

## SSRF

- `ssrf-policy.js`：allowlist、legacy IP、DNS pinning、`createPinnedDispatcher`
- `ssrf-guard.js`：对外 re-export `assertUrlSafeForFetch`
- `fetch-guard.js`：`fetchWithSsrFGuard`（每跳 pin DNS + 重定向环）
- `browser-navigation-guard.js`：`gotoWithNavigationGuard`（`page.route` 拦截）

## PlaywrightAgentSession 常用 API

| 方法 | 说明 |
|------|------|
| `roleSnapshot()` | ARIA ref 树 + `storeRoleRefsOnPage` |
| `runAct({ kind, ref, ... })` | 含 `batch`、`scrollIntoView`、`fill` fields |
| `listTabs` / `newTab` / `closeTab` / `focusTab` | 多标签 |
| `getConsoleMessages` / `getNetworkRequests` | 页面观测 |
| `armDialog` / `respondDialog` | 弹窗 |
| `goto(url)` | `gotoWithNavigationGuard` + 交互后 SSRF 复检 |

## 目录结构

```
src/infrastructure/crawl/
  index.js
  ssrf-*.js / fetch-guard.js / browser-navigation-guard.js
  playwright-session.js / pw-*.js / act-policy.js
  web-fetch-*.js / web-search-*.js / crawl-config.js
  page-screenshot-enhance.js
```

## 配置（commonconfig）

**Schema**：`core/system-Core/commonconfig/system.js` → `ai-workflow.fields.crawl`  
**默认模板**：`config/default_config/ai-workflow.yaml` → `crawl:`  
**运行时数据**：`data/server_bots/{port}/ai-workflow.yaml` → `crawl.webFetch` / `crawl.webSearch` / `crawl.browser`

**优先级**：调用方 `overrides` > `ai-workflow.crawl` > `renderer.playwright`（browser 启动）> 默认

**单一实现**：`crawl-config.js` — `resolveWebFetchRuntime` / `resolveWebSearchConfig` / `buildBrowserRuntime`  
**禁止**在 crawl 模块内读取 `process.env` 做业务配置；凭据与参数一律写 `data/server_bots/{port}/ai-workflow.yaml` → `crawl.*`。

## 与工作流

- `core/system-Core/workflow/web.js` → `web_fetch` / `web_search`
- `core/system-Core/workflow/browser.js` → 全量 `browser_*`（见 `docs/system-core.md`）

## 常见陷阱

- 扩展写在 `src/infrastructure/crawl/` 内并在 `index.js` 导出，不要在 Core 内复制一份。
- Core 业务只 import 门面，勿在 Core 写 SSRF/搜索驱动。
- 勿 `launch({ browserType: rt.browserType, ... })` 手抄字段——会丢掉 nav 超时与 SSRF。
- 探活用轻量 `launchOptionsFromBrowserRuntime()`；高清截图另开会话并尽量 **一次 goto**。

## Node 26

- `fetch-with-retry.js`、`web-fetch-executor.js` 已用全局 `fetch` + `AbortSignal.timeout`；扩展时沿用，**禁止** `node-fetch`。
- 正文/截图二进制：`toBase64()` / `Uint8Array.fromBase64()`，勿 `toString('base64')`。
- catch 与 SSRF 错误：`Error.isError` / `normalizeError`（skill **`xrk-node-runtime`**）。

## 参考

- `docs/system-core.md`（web / browser 章节）
- 本地 vendor 插件：`core/system-Core/plugin/` 下未写入 `.gitignore` 白名单的 `.js` 仍会加载，但不计入框架 baseline

