把 Scrapling 的网页抓取带给 dsh:JavaScript 渲染、结构化抽取与后台爬虫,由一个常驻 Python 进程驱动。
allow-version shared 的版本下限
profile 若用 pnpm `overrides` 钉了 `@jayyuen66/dsh-plugin-shared`,钉的值不得低于本包声明的版本。钉低了会装到一份缺少源码所需名字的产物,宿主启动时报 `N entries did not activate` 与 `does not provide an export named ...` —— 包装得上、永远不装载,任何单仓 CI 都看不见。
## 运行时能力包(macOS arm64)
包内带 `bin/dsh-scrapling.mjs`。它把能力包装进 `dataDir/runtime`(默认 `~/.dsh-scrapling`)——不写系统 Python,也不写用户的 Playwright 缓存。**没有 `postinstall`**;插件装载时不下载任何东西。
```sh
dsh-scrapling setup --feature core --apply
dsh-scrapling setup --feature browser --apply
dsh-scrapling setup --status
```
- `core` 装托管 Python 3.14.9、Scrapling 0.5 与钉死的 wheel;`browser` 再加完整 Chromium(不是 headless shell)。
- `search`、`media`、`mcp` 与 `adaptive-redis` 都以 `core` 的 venv 为载体:`mcp` 带来 `official-mcp` 模式要的官方 `scrapling-mcp` server(随钉住的 `mcp` 包进来;它的工具集由上游决定,不是本插件的面),`adaptive-redis` 带来 `adaptiveStorage: redis` 背后的 `redis` 客户端。六个包都可安装;设置卡把每个包报成 `ready`、`missing`、`corrupt` 或 `installing`。
- `--feature` 默认 dry-run;`--apply` 才落盘。`--repair` 重装且不碰用户数据;`--uninstall <包> [--purge]` 删运行时文件,只有 `--purge` 才连 `elements_storage.db` 与 `downloads/` 一起删。
- `--offline <目录>` 全程不联网:`<目录>` 里要有 Python 归档(文件名与清单 URL 的最后一段一致,浏览器与 `curl -O` 落盘的 `cpython-3.14.9+…-install_only.tar.gz` 可用,百分号转义那种写法也认),以及一个 `wheels/` 目录(每条钉版一枚 wheel,可用 `pip download --only-binary=:all: --require-hashes -r py/runtime-requirements.txt -d <目录>/wheels` 生成)。Python 归档的哈希校验与联网路径一致,pip 带 `--no-index`;缺归档或缺 wheelhouse 时报 `INSTALL_OFFLINE_ASSETS`,不会悄悄退回联网。
- `setup --status` 打印一份 JSON:`dataDir`、`profile`、`runtime`、`packs` 与 `state`。带 `--profile <目录>` 时读该 profile 的 `cordis.patch.yml`,按本包条目里的 `pythonBin`(宿主真正在用的那个设置)解析解释器;不带时 `profile.path` 为 `null`,输出里有一句说明没读 profile,`runtime` 回落到 CLI 自己的链(托管 runtime → 仓内 `_env/.venv` → PATH `python3`)。要拿 `--status` 和设置卡上的解释器对照,就带上 `--profile`。
- profile 同时有远程读面与作业后端时,设置卡能经 `scraplingRuntime` 发起同一套安装/修复/取消;否则显示可照抄的 CLI 命令。
- `integrationMode` 只在部署配置里;`setup --mode` 负责打印或写出两种 MCP 接法的客户端条目,`doctor` 用来核对一份 profile —— 见 [MCP 模式](#mcp-模式)。`official-mcp` 起的是托管 venv 里的 `scrapling-mcp`,由 `mcp` 能力包装进去(`setup --feature mcp --apply`)。
- 只验证了 **macOS arm64**。Windows、Linux 与 macOS x64 是 blocked(B1/B2):所有包都报 `blocked`,安装器直接拒绝,不猜资产路径。
##
确认它真的在工作
1. **层已挂载。** `dsh --profile web --dump-config` 能看到本包的层行。
- 这条命令是唯一被认的「它在层栈里」信号,而且它**不是只读的** —— 会把 profile 的根装配文件重写一遍。层行从 stdout 读,stderr **单独**查:未命中的 patch 目标只出现在 stderr,只看退出码证明不了任何事。`# == <层>` 与 `# == <层>, patched by <路径>` 两种形态都要认。
2. **启动日志点名解释器与注册计划。** 有一行打印探测到的能力与注册了几个工具;选中的解释器路径在装载时写进日志。解释器起得来但没装 Scrapling 时,日志点名解释器路径,并附可直接照抄的 pip 命令。
3. **设置卡渲染。** 插件详情页上有 **scrapling** 卡,全部用户可改字段按用途分组;改一个值后保存按钮亮起,写入返回 200。
4. **一次工具往返。** 让模型抓一个重 JS 的页面:结果行经官方 tool-call 视图渲染在会话里。没有浏览器的机器上,render / capture_xhr 不在模型的工具列表里,对应的提示指引也一并消失 —— 那是条件注册在起作用,不是故障。
##
它不提供什么
- **它不是搜索引擎。** 检索由 Python sidecar 走六条后端之一(`searchBackend`:`auto` / `tavily` / `bing` / `brave` / `serper` / `duckduckgo` / `searxng`);Scrapling 自己的 `google_search` 选项只是加一个 Google referer 头。每条后端都要自己的配置(`tavilyApiKey`、`braveApiKey`、`serperApiKey`、`searxngEndpoint`)—— 选中却没配的后端报 `SEARCH_AUTH`,不会静默换到别的后端。
- **npm tarball 里没有解释器与浏览器。** 它们来自你的机器,或来自可选的 CLI 安装器 —— 后者把已验证的 macOS arm64 资产装进 `dataDir/runtime`;装载时缺什么就少注册什么,不会临时去下载。
- **它不在宿主侧新增 HTTP 路由。** 设置卡是宿主的配置表单,本包没有自己的 `/api` 面。唯一真会 listen 的是 `integrationMode: mcp` 下那个 MCP server,只绑 `127.0.0.1`(`http://127.0.0.1:
在 dsh 里启用
- `dsh.bundle.patch` 指向 `cordis.patch.yml`,唯一一行注册是 `- id: scrapling`,`name` 用**裸包名** —— 宿主只扫裸包名条目的 `dsh.client` 声明。
- client 半注册两个面:设置卡(坐在 `plugins.bundle.config` 槽上,键是 bundle 包名 —— 宿主按 `entryKey: pkg.name` 派发,所以卡画在本包的详情页上),以及每个工具在 `tool.call.toolview` 上的一行结果,用官方 tool-call 视图 props 画。
- 设置命名空间就是条目 id `scrapling`;优先级为 设置卡现值 > 注册行 `config:` > schema 默认值。把一项改回默认值保存的是**清除覆盖**而不是写入同一个值,将来默认值变了你这一项也会跟着变。
- 闸门策略与 sidecar 环境在插件 `apply()` 时取一次快照 —— **设置卡的改动要等下次启动才生效**。
- 关掉某个工具时,它的 system prompt 指引段也一并消失:指引每次装配都按「那个 scope 真注册着哪些工具」现取,没装浏览器的机器少掉 render / capture_xhr 那几句,sidecar 没起来的机器一句都没有。指引段排在内置 `web_fetch` 指引旁边。
- `scrapling_crawl` 登记自己的作业类型,`job_list` 因此把它的作业标成爬虫而不是 shell 作业。
##
工具一览
十九个工具、十五枚设置开关(七个 session/state 工具共用开关),全部有测试覆盖:
| 工具 | 后端 | 需要浏览器 | 并发 |
|---|---|---|---|
| `scrapling_fetch` | `Fetcher`(curl_cffi) | 否 | 可并发;开了 `allowWriteMethods` 之后写动词转串行 |
| `scrapling_extract` | `Selector` + CSS/XPath + `adaptive` | 否 | 可并发 |
| `scrapling_locate` | 文本 / 正则 / 相似元素定位,返回生成的选择器 | 否 | 可并发 |
| `scrapling_links` | `LinkExtractor` 加下一页候选 | 否 | 可并发 |
| `scrapling_render` | `StealthySession`(patchright) | 是 | 串行;反检测开关(`solveCloudflare` / `hideCanvas` / `blockWebrtc`)也在这一条上 |
| `scrapling_capture_xhr` | 单页临时会话、页级记录 | 是 | 串行 |
| `scrapling_session_open` / `scrapling_session_fetch` / `scrapling_session_network` / `scrapling_session_state_save` / `scrapling_session_state_load` / `scrapling_session_list` / `scrapling_session_close` | Scrapling 会话类 | 仅浏览器会话 | 串行 |
| `scrapling_session_read` | 读浏览器会话当前页的 DOM | 是 | 串行 |
| `scrapling_session_act` | 会话页上的结构化动作(部署门禁) | 是 | 串行 |
| `scrapling_download` | 下载字节到 `dataDir/downloads`(部署门禁) | 否 | 可并发 |
| `scrapling_screenshot` | PNG/JPEG 截图交给 attachment 服务 | 是 | 串行 |
| `scrapling_crawl` | Scrapling Spider 框架(`content` / `sitemap` / `feed` / `shopify`),作为作业运行 | 否 | 串行 |
| `scrapling_answer` | 任一抓取路径 + `ctx.llm` | `render: true` 时是 | 串行 |
- **配额。** 同开会话至多 8 个(其中浏览器会话 3 个,每个是一个真实的 Chromium 进程)、同时在跑的爬虫 2 个、单页正文按 `*MaxOutputChars` 封顶、单个协议帧 8 MB、待处理队列 16 帧。撞上限返回明确的错误码(`SESSION_LIMIT` / `CRAWL_BUSY` / `BAD_FRAME`),由模型自己退避。
- **`scrapling_crawl` 立刻返回作业 id** —— 一次爬取是分钟级的,而工具调用有超时。用 `job_list` 看进度、`job_output` 读结果、`job_kill` 中断;这一工具要求作业后端能 attach controller,没有的 profile 干脆不注册它(单页抓取不受影响)。爬虫另有总时长上限(`crawlMaxRuntimeSeconds`),且不会爬出起始域。礼貌性默认沿用 Scrapling spider 的默认值 —— **不遵守** robots.txt、每域延迟 0、最多 4 个请求并发 —— 它是部署决定,不是某次调用的参数(见[设置项](#设置项))。
- **`scrapling_answer` 先抓页面,再带着问题交给模型**,直接拿结论而不是整页正文。默认跟随当前会话正在用的模型(读的是会话的活请求头,中途换模型也跟着变),不额外烧一份额度;要单独指定就用 `answerProvider` / `answerModel`。网页正文是**不可信输入** —— 提示把它显式框成数据,但这道防线是提示层面的:它降低风险,不等于消除风险。
### 渲染还是静态
`scrapling_render` 比 `scrapling_fetch` 慢得多、也更吃内存,所以默认从静态抓取起步。两条判据:
- **渲染结果比静态抓取短,就退回静态抓取。** 不少页面在服务端下发了可读文字,加载后又用自己的脚本把那些容器清空、换成 canvas / Lottie 动画 —— 对这类页面渲染后只会更少,任何等待设置都补不回来。这是站点行为,不是插件缺陷。
- **`waitSelector` 等的是「元素出现」,不是「内容稳定」。** 元素可能在匹配成功之后才被脚本移除,所以「选择器匹配上了」不代表那部分内容还在结果里。
还有一条不成判据但值得知道:SVG 里的坐标轴刻度**用哪个工具都抓不到** —— Markdown 转换不输出 `