# 脚本录制 **Repository Path**: wenicer/script-recording ## Basic Information - **Project Name**: 脚本录制 - **Description**: 11123213123131312 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-03 - **Last Updated**: 2026-06-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Playwright 操作录制与回放工具 这是一个基于 Playwright 程序内 Recorder API 的通用型操作录制与回放工具,不限定网站。用户可以通过本地 UI 控制台开始录制、查看脚本、回放脚本和观察日志;录制时像正常浏览器一样点击、输入、跳转、选择,工具会实时生成 Playwright 自动化脚本,并支持保存和再次回放。 ## 1. 结构说明 ```text . ├── src │ ├── index.js # CLI 入口:分发 record / run / list / help 命令 │ ├── config.js # 配置读取:路径、浏览器、超时、录制参数 │ ├── cli │ │ └── args.js # 命令行参数解析 │ ├── recorder │ │ └── codegenRecorder.js # 录制用例:通过程序内 Recorder API 静默录制并保存脚本 │ ├── runner │ │ └── scriptRunner.js # 回放用例:执行已录制的脚本文件 │ ├── ui │ │ ├── server.js # 本地 Web UI 服务与 API │ │ ├── taskManager.js # UI 任务状态与日志管理 │ │ └── public # 前端页面、样式和交互 │ ├── infrastructure │ │ ├── browser.js # 浏览器启动基础设施,保留给扩展回放策略使用 │ │ └── fileStorage.js # 文件读写、目录创建、脚本列表 │ ├── daemon # 给 Electron sidecar 调用的本地后台服务 │ ├── api # UI 和 daemon 共享的录制/回放业务服务 │ └── utils # 通用工具函数 ├── examples # Electron 主进程集成示例 ├── recordings # 默认脚本保存目录,首次录制时自动创建 ├── package.json └── README.md ``` 模块职责拆分: - `src/index.js`:只负责识别用户命令,不写具体业务逻辑。 - `src/config.js`:集中管理默认值和环境变量,避免魔法值散落。 - `src/cli/args.js`:轻量解析 CLI 参数,不引入额外依赖。 - `src/recorder/codegenRecorder.js`:录制流程,启动浏览器上下文并接入 Playwright Recorder API,不弹 Playwright Inspector。 - `src/runner/scriptRunner.js`:回放流程,使用 Node 子进程运行已保存脚本,方便捕获退出码。 - `src/ui/server.js`:本地 UI 控制台,提供录制、回放、脚本列表和日志 API。 - `src/infrastructure/fileStorage.js`:封装目录创建、文件存在性检查、脚本列表等文件操作。 ## 2. 安装与准备 ```bash npm install npx playwright install chromium ``` 如果要录制或回放 Firefox / WebKit,也可以安装全部浏览器: ```bash npx playwright install ``` ## 3. 启动 UI ```bash npm start ``` 程序会启动本地控制台并自动打开浏览器: ```text http://127.0.0.1:17391 ``` UI 支持: - 输入起始网址并开始录制 - 设置脚本文件名、浏览器和视口尺寸 - 查看已录制脚本 - 预览脚本内容 - 点击回放脚本 - 查看运行日志 - 打开脚本保存目录 ## 4. 命令行录制操作 录制任意网站,并保存为 `recordings/example.js`: ```bash npm start -- record https://example.com -o example.js ``` 也可以使用快捷脚本: ```bash npm run record -- https://example.com -o example.js ``` 执行后只会打开录制浏览器窗口,不会打开 Playwright Inspector。你在浏览器里完成点击、输入、跳转、选择等操作时,程序会在后台实时接收生成的自动化代码。录制完成后关闭浏览器,脚本会保存到指定文件。 ## 5. 命令行回放脚本 ```bash npm start -- run recordings/example.js ``` 或: ```bash npm run run -- recordings/example.js ``` 录制生成的 JavaScript 脚本会自己启动浏览器、创建上下文并执行步骤,因此回放时直接运行保存的脚本即可。 ## 6. 查看已保存脚本 ```bash npm start -- list ``` ## 7. 常用录制参数 ```bash npm start -- record https://example.com \ -o example.js \ --browser chromium \ --viewport-size 1440,900 \ --test-id-attribute data-testid ``` 支持的参数: | 参数 | 说明 | | --- | --- | | `-o, --output ` | 输出脚本路径。相对路径默认放在 `recordings` 目录 | | `-b, --browser ` | `chromium`、`firefox`、`webkit`,默认 `chromium` | | `--target javascript` | 录制脚本语言,当前回放器固定使用 `javascript` | | `--viewport-size ` | 录制视口,默认 `1280,720` | | `--test-id-attribute ` | 优先使用指定 test id 属性生成选择器 | | `--save-storage ` | 录制结束保存登录态 | | `--load-storage ` | 录制开始加载登录态 | | `--ignore-https-errors` | 忽略 HTTPS 证书错误 | ## 8. 登录态录制示例 第一次录制时保存登录态: ```bash npm start -- record https://example.com/login \ -o login-flow.js \ --save-storage recordings/state.json ``` 后续录制时加载登录态: ```bash npm start -- record https://example.com/dashboard \ -o dashboard-flow.js \ --load-storage recordings/state.json ``` ## 9. 环境变量 | 环境变量 | 默认值 | 说明 | | --- | --- | --- | | `SCRIPT_DIR` | `recordings` | 默认脚本目录 | | `SCRIPT_FILE` | `recording.js` | 默认脚本文件 | | `START_URL` | `about:blank` | 默认录制起始地址 | | `BROWSER` | `chromium` | 默认浏览器 | | `HEADLESS` | `false` | 回放扩展时可用的无头模式配置 | | `NAVIGATION_TIMEOUT_MS` | `30000` | 默认导航超时 | | `ACTION_TIMEOUT_MS` | `30000` | 动作超时 | | `SLOW_MO_MS` | `0` | 回放扩展时可用的动作慢放配置 | | `VIEWPORT_SIZE` | `1280,720` | 默认录制视口 | | `TEST_ID_ATTRIBUTE` | 空 | 选择器优先使用的 test id 属性 | | `SAVE_STORAGE` | 空 | 保存登录态文件 | | `LOAD_STORAGE` | 空 | 加载登录态文件 | | `IGNORE_HTTPS_ERRORS` | `false` | 是否忽略 HTTPS 证书错误 | ## 10. 完整代码 完整代码已经按模块写入 `src` 目录。核心入口如下: ```js const { parseCliArgs } = require('./cli/args'); const { createListConfig, createRecordConfig, createRunConfig } = require('./config'); const { recordScript } = require('./recorder/codegenRecorder'); const { runRecordedScript } = require('./runner/scriptRunner'); ``` 运行帮助可以查看完整命令: ```bash npm start -- help ``` ## 11. 多平台打包 本项目使用 `pkg` 打包 Node.js 程序,并把 Playwright Chromium 浏览器缓存复制到发布目录。最终用户拿到压缩包后,不需要安装 Node.js、npm、项目依赖或浏览器。 重要说明: - `pkg` 只能把 Node.js 代码打进可执行文件。 - Playwright 浏览器二进制必须保留在真实文件系统目录中,不能直接塞进 pkg 虚拟快照里执行。 - 所以最终发布物是一个目录/压缩包,里面包含可执行文件、启动器和 `playwright-browsers` 目录。 - 用户双击启动器即可运行,命令行零输入。 ### 11.1 package.json 打包配置 当前 `package.json` 已包含: ```json { "bin": "src/index.js", "pkg": { "scripts": [ "src/**/*.js", "node_modules/playwright/**/*.js", "node_modules/playwright-core/**/*.js" ], "assets": [ "node_modules/playwright-core/browsers.json", "node_modules/playwright-core/lib/**/*", "node_modules/playwright/lib/**/*" ], "targets": [ "node18-win-x64", "node18-linux-x64", "node18-linux-arm64", "node18-macos-arm64" ], "outputPath": "dist" } } ``` 构建脚本会额外传入: ```bash --no-bytecode --public --public-packages "*" ``` 原因是 Playwright 内部包含较复杂的 bundle 和动态加载逻辑。使用 pkg 默认 bytecode 打包时,macOS ARM64 上可能在加载 Playwright bundle 时出现 `TypeError: Invalid host defined options`。禁用 bytecode 后会把源码放入 pkg 快照,体积略大,但运行更稳定。 ### 11.2 本机平台打包 在当前平台生成可双击运行的发布包: ```bash npm run package:current ``` 输出位置: ```text dist// release/ ``` 例如 macOS Apple Silicon 会生成: ```text dist/macos-arm64/ ├── playwright-action-recorder ├── run-recorder.command ├── playwright-browsers/ ├── recordings/ └── README.txt ``` 用户双击 `run-recorder.command` 即可打开 UI 控制台。 ### 11.3 指定平台打包命令 ```bash npm run package:windows-x64 npm run package:linux-x64 npm run package:linux-arm64 npm run package:macos-arm64 ``` 支持平台: | 平台 | pkg target | 输出 | | --- | --- | --- | | Windows x64 | `node18-win-x64` | `release/playwright-action-recorder-windows-x64.zip` | | Linux x64 | `node18-linux-x64` | `release/playwright-action-recorder-linux-x64.tar.gz` | | Linux ARM64 | `node18-linux-arm64` | `release/playwright-action-recorder-linux-arm64.tar.gz` | | macOS ARM64 | `node18-macos-arm64` | `release/playwright-action-recorder-macos-arm64.tar.gz` | ### 11.4 Playwright 浏览器打包规则 打包脚本会先执行: ```bash npm run package:prepare-browsers ``` 这会把 Chromium 下载到: ```text .cache/playwright-browsers/ ``` 然后构建脚本会把它复制到发布目录: ```text dist//playwright-browsers/ ``` 运行时程序会自动设置: ```text PLAYWRIGHT_BROWSERS_PATH=<可执行文件同目录>/playwright-browsers ``` 因此最终用户不需要执行 `npx playwright install`。 ### 11.5 跨平台构建建议 Playwright 浏览器二进制有平台差异。推荐在目标平台分别构建: ```bash # Windows x64 机器 npm ci npm run package:windows-x64 # Linux x64 构建机或 x64 CI runner npm ci npm run package:linux-x64 # Linux ARM64 构建机或 ARM64 CI runner npm ci npm run package:linux-arm64 # Apple Silicon Mac npm ci npm run package:macos-arm64 ``` 如果使用 GitHub Actions,可用 matrix 分别在 `windows-latest`、`ubuntu-latest`、`macos-14` 上执行对应命令。Linux ARM64 建议使用 ARM64 runner 或 ARM64 构建机。 也可以运行下面命令查看多平台构建清单: ```bash npm run package:all ``` ## 12. 作为 Electron Sidecar 集成 本节写给客户端 A 的开发人员。推荐把本工具作为 Electron A 的 sidecar 内置进安装包,由 Electron 主进程启动和调用;不要让最终用户单独安装第二个程序。 ### 12.1 集成目标 ```text 用户打开客户端 A -> A 的主进程按需启动 recorder sidecar -> A 通过本地 HTTP API 调用录制 / 回放 / 停止 / 状态查询 -> 用户不需要安装 Node.js、npm、Playwright 或浏览器 ``` Renderer 进程不要直接访问 sidecar。推荐流程是: ```text Renderer 页面 -> ipcRenderer.invoke(...) -> Electron Main -> fetch http://127.0.0.1:/api/... -> recorder sidecar ``` 这样 token 不会暴露给前端页面,也方便主进程统一管理 sidecar 生命周期。 ### 12.2 先打包 recorder sidecar 在本项目目录执行对应平台打包命令: ```bash cd /Users/wangyang/Desktop/脚本录制 npm ci npm run package:macos-arm64 ``` 其他平台: ```bash npm run package:windows-x64 npm run package:linux-x64 npm run package:linux-arm64 npm run package:macos-arm64 ``` 打包产物示例: ```text dist/macos-arm64/ ├── playwright-action-recorder ├── run-recorder.command ├── playwright-browsers/ ├── recordings/ └── README.txt ``` 客户端 A 需要内置的是整个平台目录里的运行内容,尤其是: ```text playwright-action-recorder playwright-browsers/ recordings/ ``` Windows 平台可执行文件名是: ```text playwright-action-recorder.exe ``` ### 12.3 放入客户端 A 的安装包 推荐在客户端 A 项目中放置: ```text client-a/ ├── build/ │ └── recorder-sidecar/ │ ├── playwright-action-recorder │ ├── playwright-browsers/ │ └── recordings/ ├── src/ └── package.json ``` 如果客户端 A 使用 `electron-builder`,在 A 的 `package.json` 中增加: ```json { "build": { "extraResources": [ { "from": "build/recorder-sidecar", "to": "recorder-sidecar" } ] } } ``` 安装后,Electron 主进程中通过下面路径访问: ```js const path = require('node:path'); const executableName = process.platform === 'win32' ? 'playwright-action-recorder.exe' : 'playwright-action-recorder'; const recorderExecutable = path.join( process.resourcesPath, 'recorder-sidecar', executableName, ); ``` macOS / Linux 需要确保可执行文件有执行权限: ```bash chmod +x build/recorder-sidecar/playwright-action-recorder ``` ### 12.4 启动 sidecar daemon Electron 主进程按需启动: ```bash playwright-action-recorder daemon --port 0 ``` `--port 0` 表示自动选择空闲端口,避免和客户端 A 或其他服务冲突。 daemon 启动成功后,会向 stdout 输出一行 JSON。客户端 A 必须读取这行 JSON: ```json {"type":"ready","url":"http://127.0.0.1:51137","host":"127.0.0.1","port":51137,"token":"..."} ``` 字段说明: | 字段 | 说明 | | --- | --- | | `url` | 本地服务地址,后续 API 都基于这个地址 | | `port` | 实际监听端口 | | `token` | 本次启动生成的鉴权 token | 除 `/health` 外,所有 API 请求都必须带请求头: ```text x-recorder-token: ``` ### 12.5 Electron 主进程启动示例 本项目提供了可直接参考的主进程示例: ```text examples/electron-sidecar-main.js ``` 核心代码如下: ```js const { spawn } = require('node:child_process'); const child = spawn(recorderExecutable, ['daemon', '--port', '0'], { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true, }); child.stdout.on('data', (chunk) => { const line = chunk.toString('utf8'); const ready = JSON.parse(line); // 保存 ready.url 和 ready.token }); ``` 产品中需要注意: - 只在 Electron Main 中启动 sidecar。 - 不要在 Renderer 中保存 token。 - A 退出时必须调用 `/shutdown`。 - 如果 sidecar 异常退出,A 应该提示用户重新打开录制功能或自动重启 sidecar。 ### 12.6 API 清单 | 方法 | 路径 | 说明 | | --- | --- | --- | | `GET` | `/health` | 健康检查,不需要 token | | `GET` | `/api/state` | 当前任务状态和日志 | | `GET` | `/api/scripts` | 已录制脚本列表 | | `GET` | `/api/script?name=recording.js` | 查看脚本内容 | | `POST` | `/api/record` | 开始录制 | | `POST` | `/api/run` | 回放脚本 | | `POST` | `/api/stop` | 停止当前任务 | | `POST` | `/shutdown` | 关闭 daemon | ### 12.7 调用录制功能 Electron Main 中调用: ```js async function startRecording(sidecar) { const response = await fetch(`${sidecar.url}/api/record`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-recorder-token': sidecar.token, }, body: JSON.stringify({ startUrl: 'https://books.toscrape.com/', scriptName: 'books.js', browser: 'chromium', viewportSize: '1280,720' }), }); if (!response.ok) { throw new Error(await response.text()); } return response.json(); } ``` 请求体字段: | 字段 | 必填 | 说明 | | --- | --- | --- | | `startUrl` | 否 | 起始网址,默认 `about:blank` | | `scriptName` | 否 | 脚本文件名,默认自动生成 | | `browser` | 否 | `chromium` / `firefox` / `webkit`,默认 `chromium` | | `viewportSize` | 否 | 视口尺寸,如 `1280,720` | | `saveStorage` | 否 | 录制结束保存登录态文件 | | `loadStorage` | 否 | 录制开始加载登录态文件 | | `ignoreHttpsErrors` | 否 | 是否忽略 HTTPS 证书错误 | 录制时会打开浏览器窗口,用户完成操作后关闭录制浏览器,脚本会保存到 sidecar 的 `recordings/` 目录。 ### 12.8 调用回放功能 ```js async function runScript(sidecar, scriptName) { const response = await fetch(`${sidecar.url}/api/run`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-recorder-token': sidecar.token, }, body: JSON.stringify({ scriptName }), }); if (!response.ok) { throw new Error(await response.text()); } return response.json(); } ``` 调用示例: ```js await runScript(sidecar, 'books.js'); ``` ### 12.9 查询状态和日志 ```js async function getRecorderState(sidecar) { const response = await fetch(`${sidecar.url}/api/state`, { headers: { 'x-recorder-token': sidecar.token, }, }); return response.json(); } ``` 返回示例: ```json { "running": true, "task": { "id": "1710000000000", "type": "record", "title": "Record books.js", "startedAt": "2026-06-04T10:00:00.000Z", "status": "running" }, "logs": [] } ``` 客户端 A 可以轮询 `/api/state` 刷新 UI。建议间隔 1 到 2 秒。 ### 12.10 停止录制或回放 ```js async function stopRecorderTask(sidecar) { return fetch(`${sidecar.url}/api/stop`, { method: 'POST', headers: { 'x-recorder-token': sidecar.token, }, }).then((response) => response.json()); } ``` ### 12.11 退出 A 时关闭 sidecar Electron A 退出前调用: ```js async function shutdownRecorder(sidecar) { await fetch(`${sidecar.url}/shutdown`, { method: 'POST', headers: { 'x-recorder-token': sidecar.token, }, }).catch(() => {}); sidecar.process.kill(); } ``` 推荐挂到 Electron 生命周期中: ```js app.on('before-quit', async () => { if (recorderSidecar) { await shutdownRecorder(recorderSidecar); } }); ``` ### 12.12 Renderer 通过 IPC 调用 Renderer 不直接调用 sidecar,推荐: ```js // main.js const { ipcMain } = require('electron'); ipcMain.handle('recorder:start', async (event, params) => { return startRecording(recorderSidecar, params); }); ipcMain.handle('recorder:run', async (event, scriptName) => { return runScript(recorderSidecar, scriptName); }); ipcMain.handle('recorder:state', async () => { return getRecorderState(recorderSidecar); }); ipcMain.handle('recorder:stop', async () => { return stopRecorderTask(recorderSidecar); }); ``` Renderer 页面: ```js await window.electron.ipcRenderer.invoke('recorder:start', { startUrl: 'https://books.toscrape.com/', scriptName: 'books.js', browser: 'chromium', viewportSize: '1280,720' }); ``` ### 12.13 集成检查清单 交付客户端 A 前,请确认: - A 的安装包中包含 `recorder-sidecar/playwright-action-recorder`。 - A 的安装包中包含 `recorder-sidecar/playwright-browsers/`。 - macOS / Linux 可执行文件有执行权限。 - Electron Main 能读取 sidecar stdout 中的 ready JSON。 - API 请求带了 `x-recorder-token`。 - Renderer 只通过 IPC 调用 Main,不直接访问 sidecar。 - A 退出时调用 `/shutdown`。 - 录制、回放、停止、状态查询都在目标平台测试过。 ## 13. 常见问题 ### Playwright 浏览器不存在 如果报错类似 `Executable doesn't exist`,执行: ```bash npx playwright install chromium ``` ### 脚本回放失败 先确认脚本路径存在: ```bash npm start -- list ``` 如果页面结构变化、登录态失效或网络不稳定,录制生成的 locator 可能找不到元素。建议重新录制关键步骤,或在录制后的脚本中补充断言和等待逻辑。