DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

TC635807 /

TC635807/better-crawler4agent

Verified

When I was using agents, I found that most agents rely on curl commands to scrape web pages, which has a very low success rate and is slow, so I made this.

★ 2 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@e299c1b9

better-crawler-4-agent

License: MIT Python 3.10+ MCP Playwright

把 URL 变成干净正文的 MCP server。用真实浏览器渲染,知乎、CSDN、掘金这些"HTTP 抓回来是空壳"的站点也能拿到内容。

一个标准 stdio MCP server,不绑定任何客户端——Claude Code、Cursor、Windsurf、Cline、Codex、ZCode 都能用。

English summary

A vendor-neutral MCP server that turns URLs into clean article text using a real headless Chromium. Built for sites where plain HTTP fetching fails: JS-rendered pages and Chinese anti-bot sites like Zhihu, CSDN, and Juejin.

Scope: URL → text only. URL discovery is left to the caller, so the tool stays single-purpose and needs no search API keys.

Key features: real browser rendering, tiered fallback (browser → trafilatura → httpx+full browser fingerprint), font honeypot detection, fake-success detection (200 responses that are actually error pages), SSRF protection, and per-tier diagnostics in every response.


为什么需要它

大多数 Agent 自带的网页抓取是 HTTP 客户端 + 正文提取。对静态站点够用,但遇到下面三种情况会失败:

问题 表现
JS 渲染 返回空壳页面,只有导航和页脚
反爬拦截 知乎返回 403,CSDN 返回 521
提取退化 页面上有内容,提取出来却是零散片段

本项目用真实 Chromium 渲染,并补齐完整浏览器指纹(UA / Sec-Ch-Ua / Sec-Fetch-* 全套一致)+ 导航器覆盖。

实测对比

同一组 6 个 URL,开浏览器 vs 关浏览器(本项目 Fetcher,2026-09 实测):

成功率 知乎 CSDN 字符数 博客园字符数
有浏览器 6/6 = 100% 27069 57218 15360
无浏览器(仅静态路径) 5/6 = 83% 失败 13845(−76%) 4627(−70%)

反爬站点直接失败,其他站点正文完整度也大幅下降——没有 JS 渲染只能拿到首屏片段。

已实测可抓取的站点

知乎专栏、CSDN、博客园、掘金、豆瓣读书、少数派、腾讯云社区、React 官方文档、Python 官方文档等。单页耗时 2–5 秒(浏览器暖启动后)。


快速开始

git clone git@github.com:TC635807/better-crawler4agent.git
cd better-crawler4agent
python scripts/install.py

install.py 会自动:

  1. 在仓库内建 .venv 并安装依赖
  2. 查找并复用系统中已有的 Playwright 浏览器(核对版本,避免重复下载 ~700MB)
  3. 打印各 agent 的接入配置,复制粘贴即可

验证安装:

python scripts/selfcheck.py            # 依赖 + 浏览器 + 安全校验 + 真实抓取
python scripts/selfcheck.py --offline  # 不联网

安装选项

python scripts/install.py --print-config       # 只看配置,不安装
python scripts/install.py --reuse-env <path>   # 复用已有 Python 环境
python scripts/install.py --browsers-path <p>  # 指定已有浏览器目录
python scripts/install.py --no-browser         # 稍后自己配浏览器

接入你的 Agent

通用 mcpServers 格式

Claude Code / Cursor / Windsurf / Cline 等通用:

{
  "mcpServers": {
    "better-crawler": {
      "command": "D:\\better-crawler4agent\\.venv\\Scripts\\python.exe",
      "args": ["D:\\better-crawler4agent\\scripts\\launch.py"],
      "env": {
        "BETTER_CRAWLER_BROWSERS_PATH": "D:\\better-crawler4agent\\.playwright"
      }
    }
  }
}

路径用 python scripts/install.py --print-config 生成,它会填好你机器上的绝对路径。

配置放置位置:

客户端 位置
Claude Code 项目级 <项目>/.mcp.json,或用户级 ~/.claude.json
Cursor ~/.cursor/mcp.json
Windsurf / Cline 各自的 MCP 设置里粘贴 JSON
ZCode ~/.zcode/cli/config.json 的 mcp.servers 字段
Codex ~/.codex/config.toml → [mcp_servers.better-crawler]
DeepSeek Harness (dsh) 用仓库自带的 dsh-plugin/,见下

仓库根目录带了一份 .mcp.json(相对路径),可直接引用。

DeepSeek Harness(dsh)

dsh 的插件形态和通用 MCP 配置不同:dsh 插件是一个声明了 dsh.bundle.patch 的包。仓库根目录的 package.json 就是这个声明——整个仓库即一个 dsh bundle,里面通过 @deepseek-ai/dsh-mcp-client 把本仓库的 MCP server 注册进 profile。

dsh plugin --profile web add github:TC635807/better-crawler4agent   # 从 GitHub 装
./dsh-plugin/install.sh                                            # 本地 clone 装(外加发布技能)

dsh-plugin/cordis.patch.yml 的启动器路径由 !!js 表达式在 profile 启动时算出,安装期不需要改写任何文件。装好后模型多出三个工具:mcp__better-crawler__fetch_url、mcp__better-crawler__fetch_urls、mcp__better-crawler__crawler_status。包不带 .venv,首次使用前先在有依赖的解释器里跑一次 python scripts/install.py;配置项、解释器与浏览器路径的解析方式见 dsh-plugin/README.md。

不用 MCP,直接当库调用

import sys
sys.path.insert(0, r"D:\better-crawler4agent\src")

from better_crawler import Fetcher

async def main():
    f = Fetcher()
    await f.start()                                  # 预热浏览器
    result = await f.fetch("https://example.com/article")
    if result.ok:
        print(result.content)
        print(f"命中层级: {result.tier}, {result.content_length} 字符")
    else:
        print(f"失败: {result.error}")
    await f.close()

install.py --print-config 会打印适合你环境的这段代码。


工具

工具 说明
fetch_url(url, max_chars) 抓单个页面
fetch_urls(urls, max_chars, max_concurrent) 并发抓取,逐条独立成败,最多 20 条
crawler_status() 查浏览器状态,排查启动失败

返回结构

{
  "url": "https://...",
  "ok": true,
  "title": "页面标题",
  "content": "正文……",
  "content_length": 13845,
  "status": 200,
  "tier": "browser",
  "elapsed": 3.0,
  "error": "",
  "warning": "",
  "truncated": false,
  "attempts": [{"tier": "browser", "ok": true, "chars": 13845}]
}

设计要点是不只返回成功/失败:

  • status — 服务器返回的 HTTP 状态码,用来区分"页面不存在"(404)与"抓取被拦"(403/429)
  • tier — 命中哪条路径:browser > trafilatura > httpx
  • warning — 非空表示拿到了内容但可能不完整(如异步接口多次失败),需要调用方判断
  • content_length — 截断前的真实长度(truncated 表示返回内容被 max_chars 截过)
  • attempts — 每一层的具体结果与失败原因

失败时 error 会给出可操作的原因,而不是笼统的"抓取失败":

error 内容 含义 该怎么做
服务器返回 HTTP 404(页面不存在) URL 失效 换 URL
服务器返回 HTTP 403(服务器拒绝访问(可能触发反爬)) 被拦截 可重试
服务器返回 HTTP 502(网关错误(服务端异常)) 服务端异常 稍后重试
页面提示内容不存在或已被删除(服务端返回 200) 软 404:状态码正常但页面是错误页 换 URL
服务器返回 HTTP 200 但页面无有效正文 页面存在但没有正文(如纯图片/需登录) 视情况
域名无法解析(DNS 问题) 网络环境问题 检查 DNS/网络

最后两类值得注意:站点可能用 200 状态码渲染"页面不存在",或者页面确实没有文字内容。只看 HTTP 状态码会误判成成功,所以本工具会额外识别页面级错误特征。


抓取策略

分层降级,每层记录耗时与命中情况:

1. 浏览器渲染(主路径)
   共享单例 Chromium + 信号量限并发 + 崩溃自动重建
   冷启动 2–7s,之后单页 3–9s(含重试)
   内容疑似未渲染时自动重试,最多 3 次

2. trafilatura 直取(快路径)
   静态站约 1s;反爬站记录失败域名,10 分钟内跳过这层

3. httpx + 完整浏览器指纹(兜底)
   补 Sec-Ch-Ua / Sec-Fetch-* / HTTP2,解决 CSDN 等站的 521

浏览器不可用时(未安装等)自动降级为后两层,crawler_status 会给出原因。

自动重试:应对概率性渲染失败

有些站点(检索结果页、SPA)的内容靠接口异步填充,而接口会概率性失败——同一个 URL 反复抓,页面会在"完整内容"和"只有导航的骨架"之间随机跳。

实测万方检索页:

状态 HTML 大小 提取结果
接口失败 193 KB 727 字符(仅导航)
接口成功 481 KB 5720 字符(459 条文献)

关键是加长等待时间没用(实测等 5 秒照样失败),因为这不是渲染慢,是接口这一次没返回数据。

所以工具改成:识别骨架页 → 自动重试 → 取历次最完整的结果。

  • 判据是"HTML 结构不小(>30KB)但正文极少(<1500 字符)"
  • 确定性的失败(404、403)不重试,避免浪费时间
  • 若重试后仍只拿到少量内容,返回 ok: true 但带 warning 字段,提示内容可能不完整,由调用方判断

性能参考

实测 6 条混合 URL(含需重试的骨架页、确定性 404):

指标 数值
平均单条 ~5s
最坏单条 ~9s(三次重试)
6 条并发 3 ~13s

多数 MCP 客户端有 30 秒调用超时,因此 fetch_urls 建议一次不超过 8 条,更多请分批。

工程细节

这些是踩坑换来的,改动前请先读代码注释:

  • 单例浏览器 + 信号量 — 每 URL 起一个 Chromium 会迅速吃满内存
  • asyncio.shield + done_callback — 超时取消会让 Playwright 内部 future 异常泄漏(TargetClosedError: future exception was never retrieved)
  • 崩溃检测 + 单例重置 — Chromium 挂掉后必须重建,否则后续全部失败
  • 字体蜜罐识别 — 知乎会注入上万字符的 mmmmlli 重复串,纯按长度取正文会误选它
  • 假成功识别 — 200 响应可能是错误页、登录墙或文章已删除(知乎会渲染一张沙漠插画)
  • 骨架页重试 — 内容靠异步接口填充的站点会概率性停在空壳状态,需重试而非加长等待
  • 错误原因分类 — HTTP 状态码与 Chromium 网络错误映射成可读说明,让调用方能判断该重试还是换 URL
  • --headless=new — 缺了它,部分站点只返回空壳

配置

全部通过环境变量,都有合理默认值:

变量 默认 说明
BETTER_CRAWLER_PYTHON — 指定解释器,覆盖自动探测
BETTER_CRAWLER_BROWSERS_PATH — Chromium 所在目录
BETTER_CRAWLER_TIMEOUT 25 单页抓取超时(秒)
BETTER_CRAWLER_MAX_CHARS 50000 默认正文返回上限
BETTER_CRAWLER_ALLOW_PRIVATE 0 设为 1 放开内网限制(仅本地调试)
BETTER_CRAWLER_LOG_LEVEL INFO 日志级别(输出到 stderr)

关于 Playwright 版本

requirements.txt 锁定 playwright==1.61.0。原因是 playwright 与浏览器 revision 强绑定(1.61 → rev 1228,1.62 → rev 1234),放宽版本会让"复用已有浏览器"因 revision 不匹配而静默失败。

升级 playwright 时必须同步重装浏览器:

python -m playwright install chromium

安全

  • 仅允许 http / https,拒绝 file://、raw:// 等协议
  • 解析目标主机名,拒绝回环、内网、链路本地、云元数据地址(127.0.0.1、192.168.*、169.254.169.254 等)
  • 默认拒绝,本地调试需显式设 BETTER_CRAWLER_ALLOW_PRIVATE=1

一个"抓任意 URL"的工具本质上是本地网络请求原语。请只在可信环境启用。


项目结构

better-crawler4agent/
├── .mcp.json                    # 通用 MCP 配置(相对路径)
├── package.json                 # dsh bundle 声明:dsh.bundle.patch 指向 dsh-plugin/
├── dsh-plugin/                  # dsh 插件:bundle patch + 启动器 + 安装脚本
├── skills/web-to-text/SKILL.md  # 技能:何时用、如何选 URL、如何判断结果
├── src/better_crawler/
│   ├── browser.py               # 共享 Chromium 单例、指纹、崩溃重建
│   ├── extract.py               # 正文提取 + 蜜罐识别 + 错误页识别
│   ├── fetcher.py               # 分层编排
│   ├── errors.py                # HTTP 状态码与网络错误 → 可读说明
│   ├── safety.py                # SSRF 防护
│   └── mcp_server.py            # MCP 工具定义
├── scripts/
│   ├── launch.py                # 启动器:解析解释器与浏览器路径
│   ├── install.py               # 安装 + 打印各 agent 配置
│   └── selfcheck.py             # 自检
└── tests/test_core.py           # 单元测试(不联网)

关于 skill

skills/web-to-text/SKILL.md 是纯 Markdown,与厂商无关,内容涵盖:何时该抓取、如何挑选高质量 URL(避开 /tag/ /user/ /video/ 等聚合页)、如何判断结果是否可信。

支持 skill 的 agent(Claude Code、ZCode 等)可直接复制目录;不支持的,可把内容并入系统提示词。


开发

python -m pytest tests/ -q        # 单元测试(34 项,不联网)
python scripts/selfcheck.py       # 端到端自检(15 项,含真实抓取)

单元测试覆盖 SSRF 防护、蜜罐识别、正文提取、错误页识别。自检额外验证依赖可用性、浏览器启动与真实抓取(--offline 跳过抓取,为 12 项)。


合规

抓取请遵守目标站点的 robots.txt 与服务条款。本工具适合按需读取具体页面,不要用于高频批量抓取。


License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit e299c1b92b08

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout