内容
<!-- badges -->
[](https://pypi.org/project/shadow-web/)
[](https://github.com/ulinycoin/shadow-web/actions/workflows/test.yml)
[](https://pypi.org/project/shadow-web/)
[](LICENSE)
[](https://github.com/ulinycoin/shadow-web)
# Shadow Web
**从网页中剪切 64–97% 的令牌,然后提取结构化数据。**
开源 Python SDK,压缩 Shadow DOM,构建带有语义组的键入操作映射,修复损坏的选择器,并将 HTML 表格/表单/列表转换为干净的 JSON — 无需云支持。
```python
from shadow_web.compressor import process_html
clean_html, actions, groups = process_html(raw_html)
# ✅ actions = [{"id":"1","type":"button","label":"Buy Now","group":"Checkout"}, ...]
# ✅ 164 → 46 tokens on a typical page
from shadow_web.schema_snap import parse_page
data = parse_page(clean_html)
# ✅ tables: [{columns, types, rows, total_rows}]
# ✅ forms: [{action, method, fields: [{name, type, required, label}]}]
# ✅ lists: [{type, items, total}]
```
---
## 痛点
AI 代理需要查看网页。但是原始 HTML 充满了 `<script>`、`<style>`、内联 CSS 和交互元素,这些元素嵌套在 Shadow DOM 树中,而 Playwright 无法访问。典型的维基百科页面成本为 **99K 令牌**。您的 LLM 账单不需要这么多。
Shadow Web 是浏览器和 LLM 之间的中间层:一个压缩层,仅保留必要的内容 — 交互元素、其标签和干净的 DOM 骨架。然后,**SchemaSnap** 将干净的 HTML 转换为结构化数据,代理可以实际使用:表格行、具有验证的表单字段、列表项。
---
## 您获得的功能
| 功能 | 原始 HTML | Playwright 定位器 | Shadow Web |
|---------|----------|-------------------|------------|
| 令牌成本(维基百科) | 99,343 | — | **16,462** (−83%) |
| 令牌成本(GitHub Trending) | 167,875 | — | **37,833** (−77%) |
| Shadow DOM 可读 | ❌ | ❌ 部分 | ✅ 已展平 |
| 语义组 | ❌ | ❌ | ✅ 登录 / 购物车 / 导航 |
| 自愈选择器 | ❌ | ❌ | ✅ 本地 + LLM 回退 |
| **表格 → JSON 列 + 行** | ❌ | ❌ | ✅ SchemaSnap |
| **表单 → 具有验证的字段** | ❌ | ❌ | ✅ SchemaSnap |
| **列表 → 类型化项目** | ❌ | ❌ | ✅ SchemaSnap |
| 离线工作 | ✅ | ✅ | ✅ |
| PyPI 包 | — | `playwright` | `shadow-web` |
---
## 适用人群
| 您正在构建… | 为什么使用 Shadow Web |
|-------------------|----------------|
| 基于浏览器的 AI 代理 | 操作映射 + 自愈 + SchemaSnap = 更少的故障,结构化数据 |
| Cursor/Claude 的 MCP 工具 | 带有 **22 种工具**的内置 MCP 服务器,一键设置 |
| Playwright 刮擦器在每次部署时都会中断 | `heal_local.py` 在不产生 LLM 成本的情况下捕获 DOM 偏移 |
| 具有大量 Shadow DOM 的应用(Web 组件,Lit,Angular) | 只读展平 — 无 React/Vue 破坏 |
| **需要从网页获取数据的代理** | SchemaSnap 将表格、表单和列表解析为干净的 JSON |
---
## 快速安装
```bash
pip install shadow-web
playwright install chromium
```
**额外功能:**
```bash
pip install "shadow-web[mcp]" # Cursor/Claude MCP 服务器
pip install "shadow-web[server]" # FastAPI 修复 API
pip install "shadow-web[all]" # 所有功能
```
---
## 演示
### 黄金路径(推荐 - 本地运行)
每个步骤的令牌计数:
```bash
pip install -e ".[mcp]"
playwright install chromium
python examples/golden_path/demo.py
```
输出:原始 HTML 与 `navigate(minimal)` + `schema_session_json` + `shadow_query` — 令牌表格对比。
攻略: [examples/golden_path/CASE.md](examples/golden_path/CASE.md)
烟雾测试(安装 + 单元测试 + 一个实时网站):
```bash
bash scripts/smoke_install.sh
```
### 压缩页面(3 行代码)
```python
from shadow_web.compressor import process_html, generate_grouped_xml_map
clean_html, actions, groups = process_html(open("page.html").read())
xml_map = generate_grouped_xml_map("https://example.com", "Example", groups)
print(xml_map)
```
### Playwright + Shadow DOM 展平
```python
from playwright.sync_api import sync_playwright
from shadow_web.wrapper import ShadowPage
with sync_playwright() as p:
page = p.chromium.launch(headless=True).new_page()
page.goto("https://example.com")
shadow = ShadowPage(page)
_, xml_map = shadow.refresh()
print(shadow.capture_stats) # shadow_hosts, iframes, a11y 补充
```
### shadow_grep — 发送 LLM 需要的内容
```python
result = shadow.query("intent:login", fmt="terse")
# @1 button Sign in [Login Form]
# @2 input[email] Email [Login Form]
```
### 点击后流式传输增量
```python
shadow.refresh() # 完整基线
shadow.click("3") # 导航
_, delta_xml = shadow.refresh(diff=True) # 仅更改的内容
```
### SchemaSnap — 提取表格、表单和列表
```python
from shadow_web.schema_snap import parse_page, parse_tables
# 从页面解析所有内容
data = parse_page(raw_html)
# {
# "tables": [
# {
# "columns": ["Product", "Price", "Stock"],
# "types": ["string", "currency", "integer"],
# "rows": [["Widget A", "$19.99", "150"], ...],
# "total_rows": 12,
# "column_count": 3
# }
# ],
# "forms": [
# {
# "action": "/checkout",
# "method": "POST",
# "fields": [
# {"tag": "input", "type": "email", "name": "email",
# "required": true, "label": "Email Address"},
# {"tag": "select", "name": "country", "options": [
# {"value": "US", "label": "United States"}, ...]}
# ]
# }
# ],
# "lists": [
# {"type": "unordered", "items": ["Apples", "Bananas"], "total": 2}
# ]
# }
# 或直接导出表格行
from shadow_web.schema_snap import export_table_json, export_table_csv
records = export_table_json(clean_html, max_rows=50)
# [{"Name": "Alice", "Age": 30}, ...]
csv_text = export_table_csv(clean_html)
# "Name,Age\nAlice,30\n..."
```
### SchemaSnap 在 MCP 中
**从 HTML 字符串(无浏览器):**
| 工具 | 输出 |
|------|--------|
| `schema_table(html)` | 列 + 类型 + 行 |
| `schema_form(html)` | 字段 + 验证 |
| `schema_list(html)` | ul/ol/独立选择 |
| `schema_page(html)` | 所有结构化数据 |
| `schema_json(html)` | `[{column: value}, ...]` |
| `schema_csv(html)` | `"col1,col2\n..."` |
**从浏览器会话** (在 `navigate` / `snapshot` 之后):
| 工具 | 输出 |
|------|--------|
| `schema_session()` | 表格 + 表单 + 列表 |
| `schema_session_json()` | JSON 记录 |
| `schema_session_csv()` | CSV 字符串 |
| `get_page_html(max_chars=50000)` | 清洁 HTML(默认截断) |
所有表格工具都接受 `max_rows=50` (默认值)。如有需要,请设置 `max_rows=0` 以获取完整数据。
---
## 工作原理
```
浏览器(实时 DOM)
│
├─ [默认] DOM 捕获 — 展平 Shadow DOM + 同源 iframe(只读)
│ ↓
├─ [可选] a11y CDP 补充 — 捕获关闭的 Shadow DOM 元素
│ ↓
├─ [Chrome 145+] WebMCP 桥接 — 页面公开 document.modelContext.getTools()
│
└─→ compressor.py → 操作映射(data-sid, type, label, group)
↓
shadow_grep.py → 过滤器在 LLM 前
↓
schema_snap.py → 结构化数据(表格、表单、列表)
↓
heal_local.py → 模糊选择器恢复(无 LLM)
↓
FastAPI /v1/heal → LLM 回退 + 验证
```
**无实时 DOM 突变。** Shadow Web 读取您的页面;它永远不会写回。React/Vue/Svelte 监听器保持完整。
---
## 架构
```
shadow_web/
├── compressor.py # DOM 剥离 + 操作映射 + 语义组
├── dom_capture.py # Shadow DOM / iframe 展平(浏览器内,只读)
├── grouping.py # 语义组(表单,导航,模态)
├── schema_snap.py # 表格,表单,列表 → JSON/CSV 导出
├── heal_local.py # 本地选择器修复 + ~/.shadow-web/heal_cache.json
├── query.py # shadow_grep(类型:,意图:,标签~,AND)
├── webmcp.py # WebMCP 桥接(Chrome 145+)
├── diff.py # 页面差异(骨架 + delta XML)
├── a11y_capture.py # CDP 无障碍双重捕获
├── verified_heal.py # Playwright 选择器验证
├── wrapper.py # ShadowPage(Playwright)
├── mcp/server.py # Cursor / Claude MCP 工具
└── server/main.py # FastAPI (/v1/compress, /v1/heal)
```
---
## 基准测试
| 页面 | 原始 HTML(令牌) | 分组 XML(令牌) | 操作 | 减少 |
|------|-------------------|----------------------|---------|-----------|
| Hacker News | 8,637 | 6,704 | 227 | **−22% (1.3×)** |
| 维基百科(网页抓取) | 99,343 | 16,462 | 501 | **−83% (6.0×)** |
| GitHub Trending | 167,875 | 37,833 | 1,290 | **−77% (4.4×)** |
本地运行: `pip install tiktoken && python benchmarks/run.py`
---
## Cursor / Claude 的 MCP
一键设置:
```bash
bash scripts/cursor-setup.sh
```
或手动:
```json
{
"mcpServers": {
"shadow-web": {
"command": "shadow-web-mcp"
}
}
}
```
### 所有 22 种工具
| 类别 | 工具 | 功能 |
|----------|------|--------------|
| **浏览** | `navigate` | 打开 URL → 快照(`detail`: minimal / terse / xml / full) |
| | `snapshot` | 刷新页面;`diff=true` 仅限增量 |
| | `click`, `fill` | 通过 `data-sid` 交互 |
| **过滤(控制平面)** | `shadow_query` | 类似 grep 的过滤器在实时会话上 |
| | `query_page` | `shadow_query` 别名(JSON 输出) |
| | `shadow_grep_html` | 过滤原始/干净的 HTML 无浏览器 |
| **压缩(离线)** | `compress_html` | 剥离 + 操作映射 + 组 |
| | `compress_html_to_xml` | 分组 XML 来自 HTML |
| **数据(SchemaSnap)** | `schema_table` | 表格列 + 类型 + 行 |
| | `schema_form` | 表单字段 + 验证 |
| | `schema_list` | 列表 + 独立选择 |
| | `schema_page` | 所有结构化数据一次 |
| | `schema_json` | 表 → JSON 记录 |
| | `schema_csv` | 表 → CSV 字符串 |
| | `schema_session` | 浏览器会话中的结构化数据 |
| | `schema_session_json` | 会话中的 JSON 记录 |
| | `schema_session_csv` | 会话中的 CSV |
| | `get_page_html` | 清洁 HTML(`max_chars` 默认 50000) |
| **搜索** | `web_search` | Brave 搜索(无需 API 密钥) |
| **WebMCP** | `webmcp_list_tools` | Chrome 145+ 页面工具 |
| | `webmcp_execute_tool` | 按名称执行 WebMCP 工具 |
### 推荐的 MCP 工作流程
```
navigate(url, detail="minimal") # ~200 令牌 — action_count, page_class
schema_session_json(max_rows=50) # 数据平面 — 表记录
shadow_query("intent:login") # 控制平面 — 点击内容
click(sid) → snapshot(diff=true) # 仅限增量后操作
```
查看 [examples/golden_path/CASE.md](examples/golden_path/CASE.md) 以获取完整攻略。
---
## browser-use 集成
Shadow Web 提供与 **browser-use** (流行的代理框架)的开箱即用集成。它降低了高达 90% 的令牌使用量,并允许代理与 **Shadow DOM** 和 iframe 中的元素交互,只需一行设置。
```bash
pip install "shadow-web[browser-use]"
```
```python
from browser_use import Agent
from shadow_web import ShadowTools
# 默认格式为 "terse"(紧凑)。如有需要,请在 get_xml_action_map 中使用 format="xml"。
tools = ShadowTools(
heal_api_url="http://localhost:8000/v1/heal", # 可选:LLM 回退自愈 API
)
agent = Agent(task="...", llm=llm, tools=tools)
```
`get_xml_action_map` 也接受 `query`(例如 `intent:login`)和 `format`(`terse` | `xml`)每个调用。
查看 [examples/browser_use/](examples/browser_use/) 以获取完整的工作实现。
---
## 自愈链
```
click("3") → 绑定路径 → 元素未找到?
↓
本地修复(模糊标签 + 稳定属性匹配,85% 阈值)→ 无 LLM,无成本
↓
LLM 修复(DeepSeek / OpenAI via /v1/heal)→ 生成候选选择器
↓
选择器在无头 Chromium 中验证 → 缓存到 ~/.shadow-web/heal_cache.json
```
---
## SchemaSnap — 令牌感知数据提取
SchemaSnap 是 **数据平面** 补充 **操作映射** 控制平面:
| 层 | 问题 | 工具 |
|-------|----------|-------|
| 控制 | 可以点击什么? | `navigate`, `shadow_query`, `click`, `fill` |
| 数据 | 页面上有什么数据? | `schema_session_json`, `schema_session`, `schema_csv` |
默认 **max_rows=50** 每张表。如有需要,请设置 `max_rows=0` 以获取完整导出。
**类型推断:** `string`, `integer`, `number`, `currency`, `percentage`, `date`, `email`, `url`.
---
## 已知限制
| 限制 | 解决方法 |
|------------|------------|
| **反-bot / Cloudflare** 无头 | `page_class: Anti-bot` — 停止,不要重试;使用有头浏览器或手动步骤 |
| **`colspan` / `rowspan` 表格** | 列对齐可能会漂移;验证行形状 |
| **JS-rendered grids** (AG Grid,React Table) | 可能不使用 `<table>` — 而是使用 `shadow_query` + 操作映射 |
| **关闭的 Shadow DOM** | `navigate(..., capture_mode="dual")` 或 `"a11y"` |
| **跨源 iframe** | 不可访问 — `page_class: Iframe-heavy` |
| **令牌炸弹** | 永远不要默认为 `detail="full"`,`get_page_html(max_chars=0)` 或 `max_rows=0` 除非调试 |
---
## 不使用 Shadow Web 的情况
- 您需要 **一个** `document.querySelector` — 直接使用 Playwright。
- 您正在构建一个具有无交互的静态站点刮擦器。
- 页面是纯 HTML 且没有 Shadow DOM — 开销不值得。
---
## 许可证
MIT. 免费用于任何用途。
*如果 Shadow Web 为您节省了令牌或调试时间,请 ★ 该仓库。*
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器