GiantRavens/mdbrowse

GitHub: GiantRavens/mdbrowse

一款将网页编译为确定性 Markdown 的终端浏览器与编译器,专为人类阅读、LLM agent 消费和可引用的网页信息归档而设计。

Stars: 0 | Forks: 0

# mdbrowse **将网页编译为 markdown —— 忠实、确定、专为阅读而生。** `mdb` 是一个 web → markdown **编译器**,并在其之上构建了一个终端浏览器。 它会启动一个真实的 Chromium/Chrome 浏览器,让页面像在普通浏览器中一样执行,然后直接从引擎内部提取结构化的事实(几何信息、landmarks、计算样式 —— 绝不重新解析 HTML 字符串)。完整的 DOM 和页面交互保留在浏览器内部;人类和 agent 可以获得一个紧凑、可导航的 markdown 界面,而无需将终端空间或 LLM token 浪费在原始 HTML、脚本、布局机制和不可见的应用状态上。编译器会对每个页面的*形态*(shape)进行分类,并输出干净、层级正确的 markdown —— 相同的页面状态每次都会生成完全相同的字节。其他所有功能都是建立在这个编译器之上的前端:vim 风格的阅读器、归档存储、变更监控传感器、为 agent 服务的 MCP server、语音输出,以及基于截图的保真度 oracle。 - **以你的身份浏览。** 默认读取你的 Safari cookies —— 你会看到已登录和对你而言有付费墙的页面按你的视角渲染。`--private` 则不发送任何此类信息。 - **形态感知。** 文章输出为干净的正文;信息流(HN、新闻首页)输出为每个故事一行带链接的文本;索引卡片合并其碎片;数据表输出为 markdown pipe table(布局表保持为正文);应用形态的页面则会得到分类后的拒绝提示,而不是一堆乱码。 - **网站智能。** 瘦版移动网站(Wikipedia、Stack Overflow、Reddit)使用桌面 UA 进行捕获;Reddit 使用其 `.json` endpoint 进行无浏览器的结构化读取(未认证时使用 old.reddit HTML);Cloudflare "Just a moment…" 挑战会在捕获前等待其完成。针对特定主机的规则位于 `policy.py`(+ `~/.mdb/policy.json`)。 - **确定性且可 diff。** Front-matter 包含来源(source、retrieved、mode、shape+confidence、extractor version)和正文 content-hash。相同的页面状态 → 相同的正文。页面变得可版本化。 - **读者做主的广告策略。** Tracker 主机在网络层被拦截;第一方广告附属物(如 reddit 的推广帖子、AdSense 广告位)通过针对特定主机的策略规则(`policy.py`)被丢弃 —— 每条规则都附带其执行原因,移除情况会在 front-matter 中统计(`policy_killed`),用户规则从 `~/.mdb/policy.json` 合并,而 `MDBROWSE_NO_POLICY=1` 会关闭此层。 ## 为 agent 打造;对分析师极其锋利 LLM 按 token 付费,而原始的网页对它们收费极其残酷:原始 HTML 每个事实消耗的 token 大约是 mdb 的 9 倍,而且大多数“干净”的提取器为了保持整洁,往往会丢掉链接或破坏结构。 在基准测试套件中(七个工具在没有任何一个工具参与定义的基准事实信号上竞争),mdb 是唯一一个实现了 100% 事实召回率、且包含可导航链接和保留原始结构的提取器 —— 下方的套件表格包含详细信息。MCP server(`mdb-mcp`)将这一界面交给了任何 agent:包含来源的抓取、网页搜索、链接过滤,以及从捕获缓存中提供的分页切片。 同样的特性使其成为一款开源情报工具: - **确定性、可引用的捕获。** 相同的页面状态 → 相同的字节,带有 front-matter 来源(source URL、retrieval time、auth mode、shape + confidence、extractor version)以及正文 content-hash。捕获的内容可作为可 diff、可版本化、可引用的证据。 - **监控传感器。** `mdb watch` 保留版本化快照,且仅在发生真实变更时触发 —— 分类读取状态(ok / changed+diff / error+why),绝不仅是简单的“page fetched”。 - **可搜索的网页记忆。** 所有捕获的内容都会进入一个支持全文搜索的归档(`mdb search`、`archive_search`),并且可离线使用。 - **诚实的失败。** Bot-wall 和付费墙会作为分类后的阻挡原因返回 —— 绝不会返回伪装成页面的杂乱内容。 - **两种模式。** 以你的身份浏览(Safari cookies)查看你自己登录状态下的网页视图,或者使用 `--private` 搭配 DNT/Sec-GPC 访问匿名网络。 ## 安装 ``` brew install giantravens/tap/mdbrowse # installs mdb and mdb-mcp ``` 如果已安装 Google Chrome,mdb 会默认驱动它。如果没有 Chrome,请给 Playwright 一次性安装它自己的引擎:`playwright install chromium`。 或者选择从源码安装: ``` git clone https://github.com/GiantRavens/mdbrowse cd mdbrowse ./mdb --version # first run builds .venv (via uv) and installs Chromium ``` 项目的 `.venv` 是主机本地的。这也不是必须的:只要你将 mdb 装入其中并为该环境提供一个 Chromium,任何 Python 3.11+ 的环境都可以运行。 ## 入门指南 本节假设你已经习惯将命令复制到 Terminal 中,但不一定习惯于调试 Python、虚拟环境或浏览器自动化。 ### 你正在安装的内容 `mdb` 是一个命令行应用。你在 Terminal 中运行它,它会在后台通过真实的浏览器引擎打开网页。然后它会在你的终端中将页面显示为干净、支持键盘操作的 markdown。 这涉及三个部分: - `mdbrowse` 项目文件夹:你现在所在的源代码目录。 - 一个本地的 Python 环境:此文件夹内的 `.venv/`。它将 mdb 的 Python 包与你电脑上的其他内容隔离开来。 - 一个 Playwright Chromium 浏览器:mdb 用于捕获页面的浏览器引擎。它独立于 Safari、Chrome 和 Firefox。 ### 一次性设置 打开 Terminal 并进入项目文件夹。如果你将此仓库保存在其他地方,请改为使用该文件夹: ``` cd ~/Desktop/notebook/code/mdbrowse ``` 运行一次 mdb。首次运行将创建本地 Python 环境,将 mdb 安装到其中,并安装 mdb 需要的浏览器引擎: ``` ./mdb --version ``` 你会看到如下的首次运行设置信息: ``` mdb: first-run setup project: /Users/you/.../mdbrowse venv: /Users/you/.../mdbrowse/.venv phases: create venv -> install mdb -> install Chromium ``` 如果最终打印出了一个版本号,说明设置成功。后续的运行将复用相同的 `.venv` 并正常启动。 如果你更喜欢手动进行相同的设置: ``` uv venv uv pip install -e . .venv/bin/playwright install chromium ``` 如果你希望 `./mdb` 在无法自动构建 `.venv` 时直接失败,请设置 `MDBROWSE_NO_BOOTSTRAP=1`。 ### 使用你自己的 Python 环境 你不必使用项目的 `.venv`。如果你已经使用其他工具管理 Python 环境,请使用 Python 3.11 或更高版本,激活你的环境,然后在其中安装 mdb 及其浏览器: ``` cd ~/Desktop/notebook/code/mdbrowse uv pip install -e . python -m playwright install chromium mdb --version ``` 重要的规则是,`mdb`、Python 包和 Playwright 的 Chromium 必须安装在同一个激活的环境中。如果你使用自己的环境,请运行 `mdb ...` 而不是 `./mdb ...`;仓库根目录下的 `./mdb` 启动脚本是专门针对此 checkout 的 `.venv` 设计的。 ### 你的第一个页面 从一个体量小、可靠的页面开始: ``` ./mdb https://example.com --plain --no-pager ``` 你应该会看到一个简短的 markdown 页面。这证明 Python 环境、浏览器引擎、网络和编译器都在正常工作。 现在尝试一下交互式阅读器: ``` ./mdb https://news.ycombinator.com ``` 常用入门按键: - `j` 和 `k` 向下和向上移动。 - `Tab` 移动到下一个链接或图片。 - `Enter` 打开聚焦的链接。 - `H` 后退。 - `Space` 预览聚焦的图片,或者在未聚焦图片时滚动页面。再次按 `Space` 关闭预览。 - `?` 打开帮助。 - `q` 退出。 在大多数终端中,鼠标滚轮滚动和点击链接也是可用的。 ### 从任意位置运行 mdb 最安全的做法始终是在项目文件夹内运行 `./mdb`。 如果你的 shell 已将 `~/bin` 加入 `PATH`,那么此仓库也可以在任何文件夹中作为 `mdb` 被调用: ``` mkdir -p ~/bin ln -sf ~/Desktop/notebook/code/mdbrowse/mdb ~/bin/mdb ``` 打开一个新的 Terminal 窗口并测试: ``` mdb --version ``` 如果提示 `mdb` “command not found”,请继续在项目文件夹中使用 `./mdb`,直到你的 shell `PATH` 包含 `~/bin` 为止。 ### 常用操作 交互式阅读页面: ``` ./mdb https://www.wikipedia.org ``` 不打开阅读器直接打印页面: ``` ./mdb https://example.com --plain ``` 搜索网络: ``` ./mdb search "best explanation of zfs snapshots" ``` 将页面保存到你的个人归档: ``` ./mdb https://example.com --save ``` 监控页面以等待未来的变更: ``` ./mdb watch add https://example.com --name example ./mdb watch scan ``` 下载链接的文件: ``` ./mdb get https://example.com/file.pdf ``` ### 文件存放位置 项目文件夹包含代码。生成的用户数据会存放在更适合你操作系统的位置。 保存的页面和监控历史默认存放在: - macOS: `~/Library/Application Support/mdbrowse/archive` 和 `~/Library/Application Support/mdbrowse/watch` - Linux/BSD: `${XDG_DATA_HOME:-~/.local/share}/mdbrowse/archive` 和 `${XDG_DATA_HOME:-~/.local/share}/mdbrowse/watch` - Windows: `%LOCALAPPDATA%\mdbrowse\archive` 和 `%LOCALAPPDATA%\mdbrowse\watch` 除非你选择了其他位置,否则下载内容会存放到 `~/Downloads`。 名为 `~/mdbrowse-archive` 或 `~/mdbrowse-watch` 的旧文件夹是以前的默认路径。你可以放心地将它们移动到新的应用数据文件夹中,或者通过设置 `MDBROWSE_ARCHIVE` 和 `MDBROWSE_WATCH_DIR` 继续使用原位置。 ### 隐私基础 默认情况下,mdb 在 macOS 上会读取 Safari cookies,因此页面看起来就像你登录时看到的那样。这对于你已经拥有访问权限的网站很有用,但这也意味着 mdb 正在以你的身份浏览。 当你不想发送 Safari cookies 时,请使用私密模式: ``` ./mdb https://example.com --private ``` 保存的归档是你电脑上的普通 markdown 文件。除非你愿意在本地存储其文本,否则请不要归档私密页面。 ### 如果出现问题 如果缺少 `uv`,请先安装它。在 macOS 上使用 Homebrew: ``` brew install uv ``` 如果首次运行设置失败,请从项目文件夹中手动运行设置步骤,以便准确查看是哪个阶段失败了: ``` uv venv uv pip install -e . ``` 如果 mdb 提示缺少浏览器: ``` .venv/bin/playwright install chromium ``` 如果你使用的是自己的 Python 环境而不是 `.venv`,请运行: ``` python -m playwright install chromium ``` 如果某个网站阻止了后台浏览器,请尝试使用可见的浏览器窗口: ``` ./mdb https://example.com --headed ``` 如果页面由于登录状态而表现异常,请对比正常模式和私密模式: ``` ./mdb https://example.com ./mdb https://example.com --private ``` 如果你只是想检查已安装的副本是否仍然有效: ``` ./mdb --selftest ``` ## 使用 ``` mdb # Safari start page (bookmarks, reading list) mdb news.ycombinator.com # interactive reader (default in a terminal) mdb --plain # non-interactive render (centered; --no-center) mdb --raw # markdown document with front-matter mdb --save # archive to the mdbrowse app-data dir mdb --headed # visible real-Chrome window; verification walls (wall shape) trust it mdb --fallback-headed # retry headed only after an explicit access-denied wall mdb --speak # the page talks (macOS say; --voice, MDBROWSE_VOICE) mdb --speak-out article.aiff # page as an audio file mdb search rust atomics # web search (DuckDuckGo; MDBROWSE_SEARCH_ENGINE/URL overrides) mdb feed https://xkcd.com/atom.xml # RSS/Atom as a feed page mdb get # authenticated download (~/Downloads) mdb oracle # judge markdown fidelity against a screenshot mdb --dump bundle|manifest|body # inspect any compiler stage mdb --selftest # re-emit the fixture corpus, diff vs goldens ``` 由于 mdb 现在运行完整的 Playwright 浏览器,因此搜索默认使用 DuckDuckGo。你可以使用 `MDBROWSE_SEARCH_ENGINE=mojeek` 或 `MDBROWSE_SEARCH_ENGINE=ddg-html` 选择另一个内置引擎,或者通过 `MDBROWSE_SEARCH_URL='https://example.com/search?q={q}'` 提供自定义模板。 ### 数据位置 归档和监控存储默认位于按用户划分的应用程序数据目录下,而不是可见的主目录: - macOS: `~/Library/Application Support/mdbrowse/{archive,watch}` - Linux/BSD: `${XDG_DATA_HOME:-~/.local/share}/mdbrowse/{archive,watch}` - Windows: `%LOCALAPPDATA%\mdbrowse\{archive,watch}` `MDBROWSE_HOME` 可以重定位这两个存储位置。`MDBROWSE_ARCHIVE` 和 `MDBROWSE_WATCH_DIR` 可以单独覆盖归档或监控存储。 旧的 `~/mdbrowse-archive` 和 `~/mdbrowse-watch` 文件夹不会自动移动;如果你想继续在原位使用它们,请将它们移动到新路径或设置上述环境变量。 ### 监控传感器 —— 仅在发生真实变更时触发的版本化页面 ``` mdb watch add https://example.com/pricing --name pricing mdb watch scan # check all; commits changes to a git store mdb watch diff pricing # last change as a patch mdb watch digest # Claude narrates the week's changes (briefing material) ``` 存储:应用数据目录下的 `watch` 文件夹(使用 git;`git log -p .md` 即为页面历史)。触发器仅对**可见文本**进行 hash —— 轮换的 URL token 绝不会引发误报。 ### 阅读器 Vim 风格,在链接、图片和表单上具有单**焦点环**(类似浏览器的 Tab)。两个核心动作:**Enter = 前往,Space = 预览**(预览或关闭聚焦的图片;否则向下翻页)。每次按键的效果都可以从可见的高亮区域中准确预测出来。 搜索表单是可见的功能入口,但它们不会在页面加载时自动聚焦。按 `f` 进入提示驱动的搜索流程,或者当你希望输入字符进入该区域时,按 `Tab` 切入该字段。 | 按键 | | |---|---| | `Tab` / `S-Tab` | 下一个 / 上一个可聚焦元素 —— 全范围高亮,包含换行内容 | | `Enter` / `o` · `Space` | 前往 · 预览 | | `y` · `u` / `Y` · `d` | 复制聚焦的 URL · 复制当前 URL · 下载聚焦的目标 | | `(` `)` · `{` `}` | 标题 / 块级移动 | | `j k` `C-d C-u` `C-f C-b` `gg G` `zt zz zb` 滚动与定位 | | `/` `n` `N` | 搜索 | | `H` / `L` · `r` | 历史后退 / 前进 · 重新加载 | | `f` | 填写页面的搜索表单 (GET),并作为导航提交 | | `F` | 打开页面提供的 RSS feed | | `.` / `,` | 下一个 / 上一个检测到的页面 | | `S` / `a` | 总结 / 向此页面提问 (Claude);回答也是页面,按 `H` 返回 | | `v` | 从聚焦的元素开始朗读(再次按 `v` 停止;`--announce` 在聚焦时朗读) | | `s` · `B` · `O` | 归档 · 添加到 Safari Reading List · 在浏览器中打开 (`MDBROWSE_BROWSER`) | | `:` | URL, `s terms`, `ddg terms`, `mojeek terms`, `safari:start`, `feed:URL` | | `?` · `q` | 帮助浮层 · 退出 | 鼠标:滚轮滚动,点击跟随,点击 🖼 进行预览。(tmux:`set -g mouse on`。) ### Agent 与速度 - **MCP server** (`mdb-mcp`, 注册为 `mdbrowse`): `fetch_page`(markdown + 来源信息;长页面通过 `start_char` 进行分页,后续内容从捕获缓存中提供),`search_web`(结果作为带链接的行),`page_links`(支持 `pattern` regex 过滤),`archive_page`(返回正文 hash —— 通过对比来检测变更),`archive_search`(针对归档进行全文搜索:你的个人网页记忆),以及监控集群 —— `watch_add` / `watch_list` / `watch_scan`(结构化的读取状态:ok / changed+diff / error+why)/ `watch_diff` / `watch_remove`。 - **Agent 探针套件** (`tests/agent_probes.py`):针对 agent 实际执行动作的实时回归测试守护 —— 包括文档代码保真度、pipe table、搜索、feed 摘要、链接过滤、分页拼接、hash 确定性和快速分类失败处理。 - **Engine daemon**:在 `~/.mdb/engine.sock` 后台运行保持预热的 Chromium,首次 CLI 捕获时自动生成,空闲 30 分钟后自动退出。预热的抓取运行时间约为 0.7–1.0s。`mdb daemon start|stop|status|run`;`MDBROWSE_DAEMON=off` 可禁用。 - **浏览器执行,Token 塑形的输出**:agent 并不是在抓取 TUI 的输出记录。它们搭载与阅读器相同的真实浏览器捕获结果,但跨过 MCP 边界的只有分类后的 markdown 页面、链接、表单、来源信息以及请求的数据切片。 ## 工作原理 1. **捕获** —— 通过 Playwright 驱动 Chromium/Chrome,除非使用 `--private` 否则带上 Safari cookies,包含 stealth shim、tracker/图片/媒体拦截、自动播放抑制、内容稳定性判定、3秒 DNS 预检(黑洞域名会快速失败*并附带原因*)。`walker.js` 在页面内部运行,输出带有 landmark、类型、行内 markdown、链接和几何信息的叶子块,以及文档级别的 feed 和分页功能。绝不获取 `page.content()`。 2. **分类** —— 在输出任何内容之前,根据 bundle 信号生成低成本的形态清单(带有置信度的 `article | feed | page | app`)。 3. **输出** —— 针对特定形态的组装:重复单元检测将卡片片段折叠为每个项目一行(共享的链接目标 + 特征周期性);标题重新映射为严格的层级结构;nav/aside/footer 降级为链接列表;表单排除在文档之外(它们属于交互入口 —— 阅读器的 `f` 会从 bundle 中使用它们)。 每个阶段都可以进行检查(`--dump`),每一项变更都会在五个套件层级中进行测量: | 层级 | 守护范围 | 运行方式 | |---|---|---| | 固件语料库 (10) | 输出真相,离线,确定性 | `mdb --selftest` | | 实时探针 | 网络真相(对抗性 CDN、DNS) | `tests/live_probes.py` | | Agent 探针 | 任务真相(agent 搭载的 MCP 动词) | `tests/agent_probes.py` | | Checkin 门禁 | 固件 + 每次 commit 时扫描 11 个站点 | `tests/checkin.py` (pre-commit hook: `--install-hook`) | | 保真度 oracle | 像素真相 —— 以截图作为裁判,而非提取器本身 | `mdb oracle URL` | | 基准测试 | mdb 与其他 agent 网络工具对比:token、召回率、链接、结构、速度、确定性 | `tests/benchmark.py` | 基准测试是一种度量工具,而不是门禁:七个工具(mdb、raw HTML、tag-strip、Chromium innerText、trafilatura、pandoc、Jina reader)在没有任何一个工具参与定义的基准事实信号上进行比拼。核心数据(2026-07-05):mdb 是唯一一个实现了 100% 召回率、且包含可导航链接和保留了原始结构的竞争者;raw HTML 获取每个事实消耗的 token 约为 mdb 的 9 倍;pandoc 流水线针对整个 HN 内容仅仅输出了 2 个 token。 Checkin 门禁的网站清单(HN、CNN、BBC、apple.com、quantum.com、Wikipedia、Python 文档、IANA、GitHub、xkcd、Mojeek)断言的是*结构*(形态、项目计数、code fence、pipe table),绝不涉及内容;如果网络瘫痪,它将仅依靠固件进行门禁,而不会阻止代码提交。 ## 历史 v1(单文件 `mdbrowse.py`:fetch → strip → convert → repair)已于 2026-07-04 退役,因为 v2 编译器在各个维度上都超越了它 —— 参见 `CHANGELOG.md` 和 git 历史。其最优秀的部分(稳定性启发式算法、binarycookies 解析器、Safari 集成、tracker 列表)在 v2 中得以保留并继续发挥作用。
标签:LLM集成, Markdown转换, Web抓取, 无头浏览器, 特征检测, 逆向工具