Skill: ziniao-open/ziniao-webdriver-doc (version 1.0.0)

--- name: ziniao-webdriver-doc description: > Use when 需要理解紫鸟浏览器 WebDriver 自动化接口的定位、能力、架构或对接流程， 用于设计讨论、集成规划或工具评估。 不要用于执行具体自动化脚本编写——执行时直接参考 reference/ 中的接口与示例。 --- # ziniao-webdriver-doc（紫鸟 WebDriver 文档） ## 何时使用 / 不使用 | 场景 | 做法 | |------|------| | 需要理解紫鸟 WebDriver 是什么、能做什么 | 读本 Skill | | 需要做自动化集成/框架选型的设计决策 | 读本 Skill | | 需要查阅某个接口的请求/响应格式 | 读本 Skill 的 §按需深入 获取路径，再读对应 reference/ 文件 | | 需要查看各语言/框架示例代码 | 不读本 Skill → 直接读 `reference/framework-examples.md` | --- ## Level 0：定位与边界 ### 是什么 紫鸟浏览器 WebDriver 是紫鸟浏览器提供的本地自动化控制接口。通过命令行参数启动紫鸟浏览器进入 WebDriver 模式后，浏览器在本地开启 HTTP 服务，开发者可通过 JSON API 管理店铺窗口，并使用 Selenium / Puppeteer / Playwright / DrissionPage 等框架对接调试端口，实现对店铺浏览器的自动化控制。 ### 能力概览 | 能力域 | 说明 | |--------|------| | 客户端启动控制 | 命令行参数启动/退出紫鸟浏览器进入 WebDriver 模式 | | 认证与登录 | 通过 API 完成设备授权、账号登录、获取店铺列表 | | 店铺窗口管理 | 启动/关闭指定店铺窗口，获取调试端口(debuggingPort) | | 自动化框架对接 | 返回调试端口供 Selenium/Puppeteer/Playwright/DrissionPage 连接 | | 内核管理 | 检测/下载/更新浏览器内核 | | 缓存与会话 | 清理本地/在线缓存、管理 Cookie 策略 | | 插件管理 | 启动店铺时加载指定插件、查询插件安装状态 | ### 不做什么（边界） - 不是通用浏览器自动化方案——仅适用于紫鸟浏览器客户端 - 不提供 WebDriver 驱动本身——需根据返回的 `core_version` 自行下载匹配版本的 ChromeDriver - 不处理电商平台业务逻辑——仅提供浏览器窗口控制能力 - 不提供云端部署——仅支持本地运行（通过 `--listen_ip` 可开放远程控制） --- ## Level 1：架构与流程 ### 核心概念 **WebDriver 模式**：通过 `--run_type=web_driver` 参数启动紫鸟浏览器，使其进入自动化控制模式。此模式下浏览器在本地指定端口开启 HTTP 服务，接受 JSON 格式的 API 调用。启动前必须关闭紫鸟主进程。 **HTTP IPC 通信**：所有 API 均为 POST 请求，JSON 格式，UTF-8 编码。通过 `--port` 参数指定通信端口，地址为 `http://127.0.0.1:{port}`。每个请求需携带 `action` 字段标识操作类型、`requestId` 做全局唯一标识。超时建议 120 秒以上。 **店铺窗口（browserOauth / browserId）**：紫鸟以"店铺"为最小管理单位。调用 `getBrowserList` 获取店铺列表及加密 ID（`browserOauth`），然后通过 `startBrowser` 启动指定店铺窗口，返回 `debuggingPort` 用于自动化框架连接。若直接使用 `getBrowserList` 返回值打开店铺，只需传 `browserOauth`；仅当需要以明文店铺 ID 打开时才赋值 `browserId`。 **内核版本匹配**：`startBrowser` 返回 `core_type`（如 Chromium）和 `core_version`（如 119.1.0.16），开发者必须使用对应大版本号的 ChromeDriver（如 119.x）连接，否则自动化将失败。 ### 流程全景 ```mermaid graph LR A[申请权限] --> B["启动客户端<br>(先清理残留进程)"] B --> C["updateCore<br>检测内核<br>⚠ 需凭据"] C --> D["getBrowserList<br>获取店铺列表<br>⚠ 需凭据"] D --> E["startBrowser<br>启动店铺窗口<br>⚠ 需凭据"] E --> F["下载匹配的<br>ChromeDriver"] F --> G["连接自动化框架<br>Selenium等"] G --> H[执行自动化操作] H --> I["stopBrowser<br>关闭店铺<br>⚠ 需凭据"] I --> J[exit<br>退出客户端] ``` 1. **申请权限**：在紫鸟开放平台开通 WebDriver 权限 2. **启动客户端**：先清理残留进程（见§关键陷阱），再命令行启动紫鸟浏览器，必须参数：`--run_type=web_driver --ipc_type=http --port={端口}` 3. **updateCore**：**必须携带 company/username/password**，轮询检测内核是否就绪（statusCode==0），缺失时自动下载 4. **getBrowserList**：传入 company/username/password，获取店铺列表 5. **startBrowser**：传入 company/username/password + browserOauth，返回 debuggingPort 和 core_version 6. **下载 ChromeDriver**：根据 `core_version` 大版本号下载匹配的 ChromeDriver（**不是系统 Chrome 版本**） 7. **连接框架**：用 `127.0.0.1:{debuggingPort}` + 匹配版本的 ChromeDriver 连接 Selenium 等 8. **关闭**：`stopBrowser`（需凭据）关闭店铺窗口，`exit` 退出客户端 ### 能力摘要表 | 接口 Action | 职责 | 关键输入 | 关键输出 | 需凭据 | |-------------|------|---------|---------|:------:| | updateCore | 检测/下载内核 | **company, username, password** | statusCode, msg(进度) | **是** | | applyAuth | 设备授权 | company, username, password | statusCode | **是** | | getBrowserList | 获取店铺列表 | company, username, password | browserList[{browserOauth, browserName, siteId...}] | **是** | | startBrowser | 启动店铺窗口 | company, username, password, browserOauth/browserId | debuggingPort, core_version, core_type | **是** | | stopBrowser | 关闭店铺窗口 | company, username, password, browserOauth/browserId | statusCode | **是** | | logout | 退出登录 | 无 | statusCode | 否 | | exit | 退出客户端进程 | 无 | statusCode | 否 | | ClearCache | 清理本地缓存 | browserOauths[] | statusCode | 否 | | ClearOnline | 清理在线缓存 | company, username, password, browserOauth, type(1/2/3) | statusCode | **是** | | getPluginInstalled | 查询插件安装状态 | browserId, pluginIds | install_status | 否 | | getRunningInfo | 获取已开启的店铺 | 无 | browsers[] | 否 | ### 关键陷阱 > **以下均为实战中高频踩坑点，务必在编写自动化脚本时严格遵守。** #### 陷阱 1：凭据一致性 所有标记"需凭据"的接口（`updateCore`、`getBrowserList`、`startBrowser`、`stopBrowser`、`ClearOnline`）**每次调用都必须携带完整的 company/username/password**，不只是"登录类"接口。`updateCore` 尤其容易遗漏——它看似只是内核检测，但紫鸟服务端需要凭据验证身份后才返回内核状态。 #### 陷阱 2：错误信息处理 API 返回错误时，**优先读取 `err` 字段的内容来定位问题**，而非仅依赖 `statusCode` 数字。`err` 字段包含具体的中文错误描述，是最有价值的排障信息。 编码注意：`err` 中的中文可能因响应体未正确按 UTF-8 解码而出现乱码，导致无法定位根因。**务必确保 HTTP 客户端以 UTF-8 解码响应体**（Python `requests` 库需设置 `response.encoding = 'utf-8'`，或使用 `response.content.decode('utf-8')`）。 #### 陷阱 3：进程残留清理 紫鸟启动失败或异常退出后，可能残留多个子进程（主进程 + 内核进程），如果不清理就重启会导致端口占用、反复失败。**每次启动前和失败后重试前，必须确保所有紫鸟相关进程已终止。** 清理命令： ```bash # Windows (PowerShell) Get-Process | Where-Object { $_.ProcessName -match 'ziniao|ZiNiao' } | Stop-Process -Force # macOS / Linux pkill -f ziniao || true ``` #### 陷阱 4：ChromeDriver 版本必须匹配紫鸟内核 紫鸟浏览器的内核版本（`core_version`）**与系统安装的 Chrome 浏览器版本无关**。必须根据 `startBrowser` 返回的 `core_version` 提取大版本号（如 `119.1.0.16` → `119`），下载对应版本的 ChromeDriver。 建议策略： 1. 首次获取 `core_version` 后，检查本地缓存目录是否已有该大版本的 ChromeDriver 2. 若无，从 https://googlechromelabs.github.io/chrome-for-testing/ 下载对应版本 3. 缓存到本地目录（如 `./chromedriver_cache/{major_version}/chromedriver`），后续复用 ### 环境约定 | 项目 | 要求 | |------|------| | Windows | 紫鸟 V5 (5.X.X.X) 或 V6 (6.16.0.126+) | | macOS | 紫鸟 V6 (6.15.0.44+) | | Linux | 紫鸟 V6 (6.25.3.3+) | | 通信协议 | HTTP POST, JSON, UTF-8 | | 超时设置 | ≥ 120 秒 | | 启动前提 | 紫鸟主进程必须已完全关闭 | | 推荐框架 | Selenium（Playwright/Puppeteer 会被检测为自动化） | --- ## 按需深入（Level 2 路由表） > **规则：以下内容不要主动加载。** > 仅在你明确需要了解某个细节时，按路由表读取对应的 reference/ 文件。 | 你需要了解 | 读取文件 | |-----------|---------| | 核心工作流接口（applyAuth/getBrowserList/startBrowser） | `reference/api-core.md` | | 辅助管理接口（stopBrowser/logout/exit/缓存/插件/updateCore） | `reference/api-auxiliary.md` | | 启动客户端的命令行参数详细说明 | `reference/startup-params.md` | | 各语言/框架的示例代码与下载链接 | `reference/framework-examples.md` | | 权限开通、账号配置、常见问题排查 | `reference/prerequisites.md` |

path: reference/api-auxiliary.md
content: # 辅助管理接口

> 本文件是 ziniao-webdriver-doc 的 Level 2 参考文档。
> 仅在需要查阅 stopBrowser / logout / exit / 缓存清理 / 插件查询 / updateCore 等辅助接口时加载。

## 4. stopBrowser — 关闭店铺窗口

**请求：**
```json
{
  "company": "公司",
  "username": "用户名",
  "password": "密码",
  "action": "stopBrowser",
  "browserOauth": "店铺ID",
  "browserId": "店铺数字ID（优先取此值）",
  "requestId": "全局唯一标识",
  "duplicate": 0
}
```

**响应：**
```json
{
  "statusCode": "状态码",
  "err": "异常信息",
  "action": "stopBrowser",
  "requestId": "全局唯一标识"
}
```

**状态码：** 0=成功，-10000=未知异常，-10002=参数非法，-10003=登录失败，-10013=需设备认证

---

## 5. logout — 退出登录

**请求：**
```json
{
  "action": "logout",
  "requestId": "全局唯一标识"
}
```

---

## 6. exit — 退出客户端进程

正常退出紫鸟浏览器主进程，会自动关闭已启动店铺并保持 Cookie 等信息。

**请求：**
```json
{
  "action": "exit",
  "requestId": "全局唯一标识"
}
```

**响应：**
```json
{
  "statusCode": "状态码",
  "err": "异常信息",
  "action": "exit",
  "requestId": "全局唯一标识"
}
```

**状态码：** 0=成功，-10005=登出失败

---

## 7. ClearCache — 清理本地缓存

不需要登录即可调用。

**请求：**
```json
{
  "action": "ClearCache",
  "browserOauths": ["店铺id1", "店铺id2"],
  "requestId": "全局唯一标识"
}
```

`browserOauths` 为空则删除所有本地缓存。

**响应：**
```json
{
  "statusCode": "状态码",
  "err": "异常信息",
  "action": "ClearCache",
  "requestId": "全局唯一标识"
}
```

**状态码：** 0=成功，-10000=未知异常

---

## 8. ClearOnline — 清理在线缓存

**请求：**
```json
{
  "company": "公司",
  "username": "用户名",
  "password": "密码",
  "action": "ClearOnline",
  "browserOauth": "店铺ID",
  "type": 2,
  "requestId": "全局唯一标识"
}
```

`type` 取值：1=清除所有缓存，2=仅保留cookie，3=仅保留二步认证cookie

**响应：**
```json
{
  "statusCode": "状态码",
  "statusMsg": "具体描述",
  "err": "异常信息",
  "action": "ClearOnline",
  "requestId": "全局唯一标识"
}
```

**状态码：** 0=成功，-10000=没有找到记录

---

## 9. getPluginInstalled — 查询插件安装状态

**请求：**
```json
{
  "action": "getPluginInstalled",
  "requestId": "全局唯一标识",
  "browserId": "店铺ID",
  "duplicate": 0,
  "pluginIds": "插件id,插件id"
}
```

**响应：**
```json
{
  "statusCode": "状态码",
  "statusMsg": "",
  "err": "异常信息",
  "action": "getPluginInstalled",
  "requestId": "全局唯一标识",
  "install_status": true
}
```

`install_status`：true=已通知安装，false=未通知

**状态码：** 0=成功，-10000=没有找到记录

---

## 10. getRunningInfo — 获取当前已开启的店铺

**请求：**
```json
{
  "action": "getRunningInfo",
  "requestId": "全局唯一标识"
}
```

**响应：**
```json
{
  "statusCode": "状态码",
  "browsers": [],
  "requestId": "全局唯一标识"
}
```

**状态码：** 0=成功

---

## 11. updateCore — 更新内核

> **⚠ 此接口必须携带 company/username/password 凭据。** 遗漏凭据会导致请求失败——这是最常见的对接错误之一，因为 updateCore 看似只是"内核检测"，容易被误认为不需要登录态。

因 HTTP 有超时限制，此接口适合循环调用直到返回成功。

**请求：**
```json
{
  "company": "公司",
  "username": "用户名",
  "password": "密码",
  "action": "updateCore",
  "requestId": "全局唯一标识"
}
```

**响应：**
```json
{
  "statusCode": "状态码",
  "err": "异常信息",
  "requestId": "全局唯一标识"
}
```

**状态码：** 0=成功，-10000=处理中

**调用机制：**
1. 客户端启动后调用 updateCore，若连接失败说明客户端还未启动成功，轮询（每2秒）直到连接成功
2. statusCode==0 → 内核就绪，继续后续操作
3. statusCode!=0 → 内核缺失/下载中，继续轮询并展示 `msg` 中的下载进度
4. 建议设置总超时 300 秒，避免无限等待

**错误排查：**
- 返回错误时**优先读取 `err` 字段**获取中文错误描述，而非仅看 statusCode 数字
- 若 `err` 内容出现乱码，说明 HTTP 响应体未正确按 UTF-8 解码——务必确保客户端以 UTF-8 解码响应
- 常见失败原因：未携带凭据、凭据错误、客户端尚未完全启动


path: reference/api-core.md
content: # 核心工作流接口

> 本文件是 ziniao-webdriver-doc 的 Level 2 参考文档。
> 仅在需要查阅 applyAuth / getBrowserList / startBrowser 的请求/响应格式与状态码时加载。

## 概述

紫鸟 WebDriver 共提供 11 个 HTTP API，均为 POST 请求，JSON 格式，UTF-8 编码。
请求地址：`http://127.0.0.1:{port}`（port 为启动参数 `--port` 指定的端口）。

通用字段说明：
- `action`：接口标识（必须）
- `requestId`：全局唯一请求标识（必须）
- `statusCode`：返回状态码，`0` 表示成功
- `err`：异常信息

---

## 1. applyAuth — 申请设备授权

**请求：**
```json
{
  "company": "公司",
  "username": "用户名",
  "password": "密码",
  "action": "applyAuth",
  "requestId": "全局唯一标识"
}
```

**响应：**
```json
{
  "statusCode": "状态码",
  "err": "异常信息",
  "action": "applyAuth",
  "requestId": "全局唯一标识"
}
```

**状态码：**
| 码值 | 说明 |
|------|------|
| 0 | 成功 |
| -10000 | 未知异常 |
| -10002 | Socket参数非法 |
| -10003 | 申请流程异常 |
| -10004 | 申请失败 |

---

## 2. getBrowserList — 获取店铺列表

**请求：**
```json
{
  "company": "公司",
  "username": "用户名",
  "password": "密码",
  "action": "getBrowserList",
  "requestId": "全局唯一标识"
}
```

**响应：**
```json
{
  "statusCode": "状态码",
  "err": "异常信息",
  "action": "getBrowserList",
  "requestId": "全局唯一标识",
  "browserList": [{
    "browserOauth": "店铺ID（加密）",
    "browserName": "店铺名称",
    "browserIp": "店铺IP",
    "siteId": "店铺所属站点",
    "isExpired": false,
    "platform_id": "平台id",
    "platform_name": "平台名称"
  }]
}
```

SiteId 说明文档：https://cdn-superbrowser-attachment.ziniao.com/webdriver/doc/SiteId%E8%AF%B4%E6%98%8E20250703.txt

**状态码：**
| 码值 | 说明 |
|------|------|
| 0 | 成功 |
| -10000 | 未知异常 |
| -10002 | Socket参数非法 |
| -10003 | 登录失败（需开通webdriver权限/关闭登录验证） |
| -10004 | 获取店铺列表时服务器返回异常 |
| -10013 | 需要设备认证 |

---

## 3. startBrowser — 启动店铺窗口

连续两次调用 startBrowser 会视为重启。关闭店铺需调用 stopBrowser。

**请求：**
```json
{
  "company": "公司",
  "username": "用户名",
  "password": "密码",
  "action": "startBrowser",
  "browserOauth": "店铺ID（加密）",
  "browserId": "店铺ID（未加密，优先取此值）",
  "isHeadless": true,
  "isWaitPluginUpdate": true,
  "privacyMode": true,
  "requestId": "全局唯一标识",
  "runMode": "运行模式 1轻量/2均衡(默认)/3极速",
  "pluginIdList": "id1,id2,id3",
  "pluginIdType": 0,
  "proxyTagFiter": "name1,name2,name3",
  "cookieTypeLoad": 0,
  "cookieTypeSave": 0,
  "injectJsInfo": "{\"url\":\"http://...\",\"username\":\"用户名\"}",
  "notPromptForDownload": 1,
  "isLoadUserPlugin": true,
  "windowRatio": 100,
  "forceDownloadPath": "文件下载路径（绝对路径）",
  "preSetting": "{\"profile.managed_default_content_settings.images\": 2}"
}
```

参数说明：
- `browserOauth` / `browserId`：二选一指定目标店铺。如果直接使用 `getBrowserList` 返回的数据来打开店铺，**只需传 `browserOauth` 即可**（它已包含加密店铺标识）；仅当用户需要使用明文店铺 ID 打开时，才需要赋值 `browserId`。两者同时存在时 `browserId` 优先。
- `isHeadless`：无头模式（开启后需自行检测网络）
- `isWaitPluginUpdate`：等待插件热更新完成（仅 V5）
- `runMode`：仅 V5 支持
- `pluginIdType`：0=插件版本id，1=插件id
- `cookieTypeLoad`：固定传 0
- `cookieTypeSave`：0=默认，1=不提交
- `notPromptForDownload`：1=不弹窗，0=弹窗
- `windowRatio`：0-100，0=不控制，100=全屏
- `preSetting`：浏览器配置 JSON，支持无图模式

**响应：**
```json
{
  "statusCode": "状态码",
  "err": "异常信息",
  "action": "startBrowser",
  "browserOauth": "店铺ID",
  "requestId": "全局唯一标识",
  "LastError": "内部错误信息",
  "ip": "代理IP",
  "isDynamicIp": "是否动态IP",
  "browserPath": "允许的下载路径",
  "launcherPage": "平台默认启动页面",
  "ipDetectionPage": "IP检测页地址",
  "debuggingPort": "调试端口",
  "reportPluginId": "报表插件id",
  "duplicate": "副本标志",
  "proxyTag": "当前启动的代理标志",
  "proxyType": 5,
  "mainHandle": "窗口句柄",
  "core_version": "内核版本",
  "core_type": "内核类型",
  "coreVersion": "内核版本",
  "coreType": "内核类型",
  "downloadPath": "文件下载路径"
}
```

`proxyType` 取值：-1=错误数据，0=站群，1=云平台，2=自有IP，5=本地IP

使用 Selenium 连接时需根据 `core_type` 及 `core_version` 选取对应版本 ChromeDriver：
```java
ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("debuggerAddress", "127.0.0.1:" + debuggingPort);
```

**状态码：**
| 码值 | 说明 |
|------|------|
| 0 | 成功 |
| -10000 | 未知异常 |
| -10001 | 内核窗口创建失败 |
| -10002 | Socket参数非法 |
| -10003 | 登录失败 |
| -10004 | browserOauth缺失 |
| -10006 | 上次 startBrowser 还未执行结束 |
| -10013 | 需要设备认证 |
| 1 | 初始化数据失败 |
| 2 | 当前IP无法正常使用 |
| 4 | 初始化时区失败 |
| 5 | 初始化代理失败 |
| 6 | 初始化黑白名单失败 |
| 7 | 启动内核失败 |
| 8 | 初始化浏览器个人目录失败 |
| 9 | 初始化Cookies失败 |
| 11 | 初始化浏览器设置文件失败 |
| 13 | 初始化代理信息配置失败 |

`LastError` 字段包含具体错误信息。


path: reference/framework-examples.md
content: # 自动化框架示例

> 本文件是 ziniao-webdriver-doc 的 Level 2 参考文档。
> 仅在需要查看各语言/框架的示例代码与下载链接时加载。

## 概述

紫鸟 WebDriver 支持多种自动化框架和编程语言。所有示例代码均需替换以下信息后运行：
- 驱动程序路径（ChromeDriver 等）
- 客户端可执行程序路径
- Socket 端口号
- 用户登录信息（company, username, password）

**推荐使用 Selenium**：Playwright 和 Puppeteer 会被平台检测为自动化，需自行处理特征问题。

## 框架支持矩阵

| 框架 | 语言 | 示例文件 | 下载链接 | 备注 |
|------|------|---------|---------|------|
| Selenium | Python | ziniao_webdriver_http_py3.py | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/ziniao_webdriver_http_py3.py) | 基础店铺访问示例，适合初学者 |
| Selenium | Python | ziniao_webdriver_demo | [GitHub](https://github.com/ziniao-open/ziniao_webdriver_demo) | 开源示例集：订单拉取、报表下载、评价导出 |
| Selenium | Java | webdriver-java-demo.zip | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/webdriver-java-demo.zip) | 用 IDEA 打开后运行 |
| Selenium | C#.NET | webdriver-C#.net-demo.zip | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/webdriver-CSharp-demo.zip) | Windows 环境 |
| Puppeteer | JavaScript | puppeteer_js_demo.zip | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/puppeteer_js_demo.zip) | 通过 Puppeteer API 控制 |
| Playwright | Python | ziniao_playwright_http_py3.py | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/ziniao_playwright_http_py3.py) | 会被检测为自动化，建议用 Selenium |
| Playwright | Java | playwright-java-demo.zip | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/playwright-java-demo.zip) | 会被检测为自动化，建议用 Selenium |
| Playwright | JavaScript | playwright_js_demo.zip | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/playwright_js_demo.zip) | 会被检测为自动化，建议用 Selenium |
| DrissionPage | Python | ziniao_drissionpage_http_py3.py | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/ziniao_drissionpage_http_py3.py) | 通过 DrissionPage API 控制 |

## 连接示例（以 Selenium 为例）

### Python + Selenium

```python
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service

# startBrowser 返回的 debuggingPort 和 core_version
debugging_port = 12345
core_version = "119.1.0.16"

# 根据 core_version 的大版本号选择对应 ChromeDriver
chrome_driver_path = "/path/to/chromedriver-119"

options = Options()
options.add_experimental_option("debuggerAddress", f"127.0.0.1:{debugging_port}")

service = Service(executable_path=chrome_driver_path)
driver = webdriver.Chrome(service=service, options=options)

# 现在可以操作店铺浏览器窗口
print(driver.title)
```

### Java + Selenium

```java
ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("debuggerAddress", "127.0.0.1:" + debuggingPort);

// 根据 core_version 选择对应版本 ChromeDriver
System.setProperty("webdriver.chrome.driver", "/path/to/chromedriver-" + majorVersion);

WebDriver driver = new ChromeDriver(options);
System.out.println(driver.getTitle());
```

## ChromeDriver 版本匹配

> **⚠ 紫鸟内核版本 ≠ 系统 Chrome 版本。** 不要使用系统安装的 Chrome 浏览器对应的 ChromeDriver，必须根据紫鸟 `startBrowser` 返回的 `core_version` 来匹配。

`startBrowser` 响应中的 `core_type` 和 `core_version` 决定了需要使用的 ChromeDriver 版本：
- `core_type: "Chromium"`, `core_version: "119.1.0.16"` → 使用 ChromeDriver 119.x
- 下载地址：https://googlechromelabs.github.io/chrome-for-testing/
- JSON API（自动化脚本可直接解析）：https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json

版本不匹配会导致连接失败，务必检查。

### 自动下载与缓存策略（Python 伪代码）

```python
import os, re, json, zipfile, io, platform
import requests

CACHE_DIR = "./chromedriver_cache"

def get_chromedriver_path(core_version: str) -> str:
    """根据紫鸟 core_version 获取匹配的 ChromeDriver 路径，自动下载缓存。"""
    major = core_version.split(".")[0]  # "119.1.0.16" → "119"
    
    exe_name = "chromedriver.exe" if platform.system() == "Windows" else "chromedriver"
    cached = os.path.join(CACHE_DIR, major, exe_name)
    
    if os.path.exists(cached):
        return cached
    
    # 查询可用版本
    resp = requests.get(
        "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
    )
    resp.encoding = "utf-8"
    data = resp.json()
    
    # 找到该大版本的最新 chromedriver
    plat = {
        "Windows": "win64", "Darwin": "mac-x64", "Linux": "linux64"
    }[platform.system()]
    
    download_url = None
    for v in reversed(data["versions"]):
        if v["version"].startswith(major + ".") and "chromedriver" in v["downloads"]:
            for d in v["downloads"]["chromedriver"]:
                if d["platform"] == plat:
                    download_url = d["url"]
                    break
            if download_url:
                break
    
    if not download_url:
        raise RuntimeError(f"未找到大版本 {major} 的 ChromeDriver 下载地址")
    
    # 下载并解压
    os.makedirs(os.path.join(CACHE_DIR, major), exist_ok=True)
    zip_resp = requests.get(download_url)
    with zipfile.ZipFile(io.BytesIO(zip_resp.content)) as zf:
        for name in zf.namelist():
            if name.endswith(exe_name):
                with open(cached, "wb") as f:
                    f.write(zf.read(name))
                break
    
    if platform.system() != "Windows":
        os.chmod(cached, 0o755)
    
    return cached
```

### 缓存目录结构

```
chromedriver_cache/
├── 119/
│   └── chromedriver.exe
├── 120/
│   └── chromedriver.exe
└── ...
```

## GitHub 开源示例项目

仓库地址：https://github.com/ziniao-open/ziniao_webdriver_demo

包含以下场景示例：
1. **Demo 1**：电商订单自动拉取（热门推荐）
2. **Demo 2**：销售与流量业务报表下载
3. **Demo 3**：电商平台用户评价导出

克隆后替换配置信息即可运行。


path: reference/prerequisites.md
content: # 前置条件与排障

> 本文件是 ziniao-webdriver-doc 的 Level 2 参考文档。
> 仅在需要了解权限开通、账号配置、常见问题排查时加载。

## 概述

使用紫鸟 WebDriver 前需完成权限开通和环境准备。本文档覆盖权限申请、账号配置和常见问题排查。

## 1. 开通 WebDriver 权限

首次使用 WebDriver 时，需先在紫鸟开放平台开通权限。

详细步骤见官方文档：[如何开通webdriver权限](https://open.ziniao.com/docSupport?docId=99)

## 2. 客户端版本要求

| 操作系统 | 最低版本 |
|---------|---------|
| Windows | 紫鸟 V5 (5.X.X.X) 或 V6 (6.16.0.126+) |
| macOS | 紫鸟 V6 (6.15.0.44+) |
| Linux | 紫鸟 V6 (6.25.3.3+) |

## 3. 环境准备清单

### 3.1 关闭主进程

启动 WebDriver 前**务必**确保紫鸟浏览器主进程已完全关闭，避免端口占用或冲突。

### 3.2 清理残留进程

紫鸟启动失败或异常退出后，可能残留多个子进程（主进程 + 内核进程），不清理直接重启会导致端口占用、反复失败。**每次启动前和失败后重试前，都必须执行清理。**

已知的紫鸟相关进程名：`ziniao`、`ZiNiao`、`ziniao.exe`、`ZiNiaoBrowser` 等（不同版本命名可能不同，建议用模糊匹配）。

清理命令：

```bash
# Windows (PowerShell)
Get-Process | Where-Object { $_.ProcessName -match 'ziniao|ZiNiao' } | Stop-Process -Force

# macOS / Linux
pkill -f ziniao || true
```

验证清理结果：

```bash
# Windows (PowerShell) — 应无输出
Get-Process | Where-Object { $_.ProcessName -match 'ziniao|ZiNiao' }

# macOS / Linux — 应无输出
pgrep -f ziniao
```

### 3.3 创建自动化专用账号

- 创建专用的自动化成员账号（命名建议：`自动化_Robot`）
- 正式使用前验证该账号能否正常登录紫鸟浏览器客户端

### 3.4 配置账号权限

**问题场景**：自动化账号登录后无法获取店铺列表。

**解决方案**：
1. 登录企业管理后台
2. 检查该账号的权限配置
3. 授予查看对应账号/店铺的权限
4. 建议新建包含此权限的自定义角色并分配给自动化账号

### 3.5 超时设置

将所有 HTTP 请求的超时时间设为 **120 秒或以上**，因为启动店铺环境可能耗时较长。

### 3.6 系统资源管理

- 启动店铺窗口 CPU 消耗较大
- 根据设备配置合理控制并发启动数量
- 建议错开各任务的启动时间，避免瞬时负载过高

### 3.7 ChromeDriver 版本管理

> **关键：紫鸟内核版本 ≠ 系统 Chrome 版本。** 必须根据 `startBrowser` 返回的 `core_version` 匹配 ChromeDriver，不能使用系统 Chrome 对应的版本。

**匹配规则：**
- `core_version` 格式为 `X.Y.Z.W`（如 `119.1.0.16`），取第一段 `X` 作为大版本号
- 下载该大版本号对应的 ChromeDriver（如大版本 `119` → ChromeDriver 119.x）

**下载地址：**
- Chrome for Testing：https://googlechromelabs.github.io/chrome-for-testing/
- JSON API（便于自动化脚本解析）：https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json

**本地缓存建议：**

```
chromedriver_cache/
├── 119/
│   └── chromedriver.exe    # 对应 core_version 119.x
├── 120/
│   └── chromedriver.exe    # 对应 core_version 120.x
└── ...
```

自动化脚本应实现以下逻辑：
1. 从 `startBrowser` 响应中提取 `core_version` 大版本号
2. 检查缓存目录 `chromedriver_cache/{major_version}/` 是否存在
3. 若不存在，从 Chrome for Testing 下载对应版本并解压到缓存目录
4. 使用缓存中的 ChromeDriver 路径连接 Selenium

## 4. 通信注意事项

1. 请求/响应均为 JSON 格式，收发消息统一 UTF-8 编码
2. HTTP 模式下超时时长必须 ≥120 秒
3. 调用任何接口后务必检查 `statusCode`（0 表示成功），**出错时优先读取 `err` 字段获取中文错误描述**，这是最有价值的排障信息
4. **启动后必须首先调用 `updateCore` 接口**（需携带凭据），否则打开店铺可能出现内核缺失
5. **务必确保 HTTP 客户端以 UTF-8 解码响应体**，否则 `err` 中的中文会乱码，严重影响错误定位（Python `requests` 库需设置 `response.encoding = 'utf-8'`）
6. 所有标记"需凭据"的接口（`updateCore`、`getBrowserList`、`startBrowser`、`stopBrowser`、`ClearOnline`）每次调用都必须携带 company/username/password

## 5. 常见问题

| 现象 | 可能原因 | 解决方案 |
|------|---------|---------|
| 连接接口失败 | 客户端未启动完成 | 轮询 updateCore 直到连接成功 |
| getBrowserList 返回 -10003 | 未开通权限或未关闭登录验证 | 检查权限开通状态 |
| getBrowserList 返回 -10013 | 新设备未授权 | 调用 applyAuth 进行设备授权 |
| 店铺列表为空 | 账号无店铺权限 | 在企业管理后台配置角色权限 |
| startBrowser 返回 -10006 | 上次启动未完成 | 等待或调用 stopBrowser 后重试 |
| startBrowser 返回 7 | 内核缺失 | 确保先调用 updateCore 等待成功 |
| Selenium 连接失败 | ChromeDriver 版本不匹配 | 根据 core_version 下载匹配版本 |
| 请求超时 | 超时设置过短 | 设置超时 ≥120 秒 |
| 多账号冲突 | 端口/缓存冲突 | V5 使用 `--multip` + `--enforce-cache-path` |
| updateCore 返回错误 | 未携带凭据（company/username/password） | 所有需凭据的接口必须携带完整凭据 |
| err 字段内容乱码 | HTTP 响应体未以 UTF-8 解码 | 设置 `response.encoding = 'utf-8'` 或用 `response.content.decode('utf-8')` |
| 重启失败 / 端口占用 | 上次失败后残留紫鸟进程 | 清理所有紫鸟相关进程后重试（见§3.2） |
| Selenium 连接失败（版本不匹配） | 使用了系统 Chrome 对应的 ChromeDriver | 必须根据紫鸟 `core_version` 大版本号下载匹配的 ChromeDriver（见§3.7） |

## 6. 技术支持

如在对接过程中遇到问题，可通过以下渠道获取支持：
- 微信添加紫鸟生态管家，备注 "紫鸟webdriver"
- 官方 FAQ：https://open.ziniao.com/docSupport?docId=257


path: reference/startup-params.md
content: # 启动参数详细说明

> 本文件是 ziniao-webdriver-doc 的 Level 2 参考文档。
> 仅在需要了解紫鸟浏览器 WebDriver 模式的启动命令行参数时加载。

## 概述

通过命令行参数启动紫鸟浏览器客户端，使其进入 WebDriver 模式。
启动前**必须**关闭紫鸟浏览器的主进程，否则可能端口占用或冲突。

## 必选参数

| 参数 | 版本支持 | 说明 |
|------|---------|------|
| `--run_type=web_driver` | V5 / V6 全平台 | 指定以 WebDriver 模式运行主进程 |
| `--ipc_type=http` | V5 / V6 全平台 | 约定通信方式为 HTTP，超时建议 ≥120 秒 |
| `--port=端口号` | V5 / V6 全平台 | 指定通讯端口，紫鸟在本机以此端口启动 HTTP 服务 |

## 可选参数

| 参数 | 版本支持 | 说明 |
|------|---------|------|
| `--port_exception=1886,1567` | V5 only | 店铺内允许连接到本地的端口号，逗号分隔 |
| `--multip` | V5 only | 去除干扰自动化的功能，多账号建议与 `--enforce-cache-path` 一起使用 |
| `--enforce-cache-path=路径` | V5 only | 指定独立缓存目录，允许多账号同时运行，需自行调整 `--port` 避免冲突 |
| `--listen_ip=IP` | V5 only | 设置监听地址，默认 127.0.0.1。非本机时允许远程控制 |
| `--dns=DNS服务器` | V5 only | 设置 DNS，默认使用本机 DNS。示例：`--dns=223.5.5.5` |

## 启动命令示例

### Python

```python
import subprocess, platform, time

client_path = "紫鸟浏览器可执行文件路径"
socket_port = 9515

is_windows = platform.system() == 'Windows'
is_mac = platform.system() == 'Darwin'
is_linux = platform.system() == 'Linux'

def start_browser():
    try:
        if is_windows:
            cmd = [client_path,
                   '--run_type=web_driver',
                   '--ipc_type=http',
                   '--port=' + str(socket_port)]
        elif is_mac:
            cmd = ['open', '-a', client_path, '--args',
                   '--run_type=web_driver',
                   '--ipc_type=http',
                   '--port=' + str(socket_port)]
        elif is_linux:
            cmd = [client_path, '--no-sandbox',
                   '--run_type=web_driver',
                   '--ipc_type=http',
                   '--port=' + str(socket_port)]
        else:
            exit()
        subprocess.Popen(cmd)
        time.sleep(5)
    except Exception:
        import traceback
        print('start browser process failed: ' + traceback.format_exc())
        exit()
```

### JavaScript (Node.js)

```javascript
const childProcess = require('child_process');
const util = require('util');
const os = require('os');
const execFile = util.promisify(childProcess.execFile);

const clientPath = '紫鸟浏览器可执行文件路径';
const socketPort = 9515;

const is_windows = os.platform() === 'win32';
const is_mac = os.platform() === 'darwin';
const is_linux = os.platform() === 'linux';

async function startBrowser() {
    try {
        if (is_windows) {
            const cmd = [clientPath,
                '--run_type=web_driver',
                '--ipc_type=http',
                `--port=${socketPort}`];
            execFile(cmd[0], cmd.slice(1));
        } else if (is_mac) {
            const cmd = ['open', '-a', clientPath, '--args',
                '--run_type=web_driver',
                '--ipc_type=http',
                `--port=${socketPort}`];
            execFile(cmd[0], cmd.slice(1));
        } else if (is_linux) {
            const cmd = [clientPath, '--no-sandbox',
                '--run_type=web_driver',
                '--ipc_type=http',
                `--port=${socketPort}`];
            execFile(cmd[0], cmd.slice(1));
        } else {
            process.exit();
        }
        await new Promise(resolve => setTimeout(resolve, 5000));
    } catch (error) {
        console.log(`start browser process failed: ${error}`);
        process.exit();
    }
}
```

## 各操作系统差异

| 操作系统 | 启动方式 | 特殊参数 |
|---------|---------|---------|
| Windows | 直接调用可执行文件 | 无 |
| macOS | 通过 `open -a` 启动，`--args` 传参 | 无 |
| Linux | 直接调用可执行文件 | 需追加 `--no-sandbox` |


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)