本页内容
我们 amazon-product 的每一个测试都通过了。而这个工具返回的是:价格货币 null、评分 null、每一张图片 null,本该是品牌的位置上写着字符串 "Brand: Amazon"。CI 里什么都不像坏了,因为那些 fixture 是照着选择器写的,不是照着网站写的。
CrawlForge MCP v5.2 的大部分内容都是这个形状。两天里,我们把 28 个工具全部放到真实网站上跑了一遍——维基百科、Hacker News、Amazon、Shopify 店铺、npm、YouTube、GitHub、Reddit,还有一份美国国税局的 PDF——然后发布了六个版本:v5.2.0 到 v5.2.5,外加共享包 crawlforge-extractors 的四个版本。下面是全部内容,按它为你解决了什么来组织,而不是按版本号。
工具数量仍是 28 个。只有一个价格变动。没有任何重命名,也没有任何输出结构变化,所以升级是无缝的。
目录
- 本次发布一览
- 为什么这么多修复同时落地
- 商品数据:Shopify、Amazon、npm、YouTube
- PullPush 停摆之后的 Reddit 全站搜索
- 真的会触发的变更追踪
- 抓取保真度:被误删的内容
- 本地 LLM:Ollama 终于能用了
- 浏览器自动化与 stealth 卡死
- 语言检测与 PDF 表格
- 基础设施:缓存、传输、Chromium
- 提取器只留一份
- credits 成本
- 如何升级
本次发布一览
| 变更 | 工具 | 版本 |
|---|---|---|
新增 shopify-product 模板,直接读店铺自己的 JSON | scrape_template | 5.2.0 |
| 模板可以请求机器可读接口,而不是 HTML | scrape_template | 5.2.0 |
amazon-product 按真实页面重建 | scrape_template | 5.2.0 |
响应中新增 responseTime | fetch_url | 5.2.0 |
响应中新增 cached 和 crawled_at | crawl_deep | 5.2.0 |
通过 OLLAMA_API_KEY 支持远程 Ollama 接入点 | 依赖 LLM 的工具 | 5.2.0 |
| 价格变动按幅度打分,而不是按占页面的比例 | track_changes | 5.2.0 |
customSelectors 真正能限定比较范围 | track_changes | 5.2.0 |
| 框架流式渲染的内容不再被删除 | scrape | 5.2.0 |
| 脚本内容不再被算作页面正文 | scrape | 5.2.0 |
| LLM 回显 schema 会明确报错,而不是当成数据返回 | scrape | 5.2.0 |
| Ollama 注册为 LLM 提供方;自动挑选已安装的最佳模型 | extract_structured、deep_research | 5.2.0 |
| 修复七处 Playwright 缺陷 | scrape_with_actions | 5.2.0 |
| 渲染进程泄漏与卡死恢复 | stealth_mode | 5.2.0 |
磁盘缓存配置生效;crawl_deep 只在内存中缓存 | 所有带缓存的工具 | 5.2.0 |
shopify-product 出现在模型读取的工具描述里 | scrape_template | 5.2.1 |
structuralSimilarity 可以给出低于 0.5 的分数 | track_changes | 5.2.1 |
| 布局表格转换为 markdown,而不是原样透传 HTML | scrape、extract_text | 5.2.2 |
| 中文、希腊语、阿拉伯语、挪威语和马来语变为可检测 | analyze_content | 5.2.2 |
| Reddit 全站关键词搜索改用「网络发现 + 归档填充」重建 | reddit_search | 5.2.3 |
npm-package 改读注册表文档,不再抓网页 | scrape_template | 5.2.3 |
reddit_search 从 2 credits 调整为 5 credits | reddit_search | 5.2.4 |
customSelectors 对非 div 标签重新建立索引;被忽略的比较选项会被报告 | track_changes | 5.2.5 |
youtube-video 返回真实播放量,并新增点赞数 | scrape_template | extractors 1.2.1 |
| PDF 表格提取终于返回它本就取到的表格 | process_document | 托管 API |
为什么这么多修复同时落地
因为我们不再信任测试套件,转而把工具对准真实网站。
上面 amazon-product 的失败是最清楚的例子。有六个单元测试覆盖这个模板,六个全过——因为它们用到的每一个选择器(priceCurrency meta 标签、#acrPopover .a-size-base、img.a-thumbnail-image)在今天的 Amazon 上根本不存在,而 fixture 的 HTML 是照着它们写出来的。那些测试验证的只是「我们的代码和自己一致」。
同样的模式反复出现。scrape_with_actions 有七处 Playwright 缺陷,套件完全看不见,因为它的假页面会实现执行器调用的任何东西——包括 Playwright 根本没有的 API。youtube-video 模板读的是 YouTube 观看页上压根不存在的属性,而它的 fixture 让测试一直是绿的。process_document 读的是 PDF 库声明了却从不写入的字段。
所以新的测试套件都是先写成「对修复前的代码必然失败」——amazon-product 24 个里有 15 个,crawl_deep 缓存作用域 10 个里有 8 个,scrape_with_actions 的 Playwright API 有 7 个。套件现在是 1,122 个单元测试,28 个工具的 MCP 协议合规度 100%;但真正重要的是上面那句:下面每一处修复,都是被真实页面发现的,不是被 CI 发现的。
商品数据:Shopify、Amazon、npm、YouTube
scrape_template 有四个模板发生了变化,其中三个方向一致:别再解析渲染后的页面,改读网站本就公开的结构化数据。
shopify-product,全新模板
Shopify 的 Dawn 主题会无条件地把每一个价格标签都写进 HTML,再用组件 CSS 把不适用的隐藏起来。于是读 DOM 的抓取器会在一个库存一百件的商品上看到「Sold out」。而如果你让 LLM 去读一个本就不存在的划线价,它会给你编一个像模像样的——在我们的测试里它编出了 27.99。
Shopify 在 /products/<handle>.json 上以 JSON 提供同一份数据,所有店铺都有,包括使用自有域名的,所以这个模板直接读它:
{
"tool": "scrape_template",
"params": {
"template": "shopify-product",
"url": "https://shop.example.com/products/kelpie-bandana"
}
}你会拿到精确价格、划线价、on_sale、货币、各变体的价格区间、分变体库存、选项、图片和标签——不解析任何 HTML,链路里也没有 LLM。真实抓取中遇到的店铺差异都已处理:划线价缺失时,有的店是 "",有的店是 "0.00"(两者都读作 null,因为两者都不渲染标签;而真正免费的商品仍保留它 0.00 的价格);标签可能是数组,也可能是逗号连接的字符串;由于该接口不带 available 字段,库存由库存管理方式、超卖策略和数量推导得出——当 payload 没说清楚时返回 null,而不是猜「有货」。
要做到这一点,需要在更底层加一个改动:TemplateRegistry 新增了两个可选钩子,resolveUrl(url) 用于改写工具那唯一一次请求的目标,extractRaw(body, url) 用于解析非 HTML 响应。请求本身仍由工具持有并受 SSRF 防护,发生改写时会额外报告 fetchedUrl。HTML 模板不受影响。
amazon-product,重建版
选择器全部从三个真实页面的抓取样本里重新推导——一件 Amazon 自营设备、一家品牌店铺,以及一本书:
- 货币取自加入购物车表单的隐藏字段,而非 meta 标签。
- 评分从
#acrPopover的title属性解析为数字,所以你拿到的是4.7,而不是"4.7 out of 5 stars"。 - 评论数同时兼容
"(198,594)"和"198,594 global ratings"两种写法——Amazon 两种都在用——返回198594。 - 品牌把三种署名格式统一还原成纯品牌名。
- 图片去掉 URL 里 Amazon 的尺寸标记。带标记的是 1 KB 缩略图,去掉之后同一个 URL 就是 16 KB 的原图。
npm-package,改读注册表
npmjs.com 对普通 HTTP 请求一律回 403;偶尔拿到响应体时,那些依赖类名片段的选择器也早已匹配不上——版本返回 null,周下载量返回 null,repository 指向的是 stargazers 链接。现在它会把包页面解析到对应的注册表文档并读取那份数据,返回版本、许可证、仓库地址、主页、维护者、keywords、依赖以及任何弃用声明。注册表里的仓库地址形如 git+ssh://、git+https://、git:// 或裸的 owner/repo,一律规范化为可浏览的 https。
周下载量则是直接省略,而不是返回 null,这是有意为之。它位于另一个独立接口上;而注册表里那个能连同包数据一起给出下载量的接口,本质上是一个搜索接口:你问它 left-pad,它会回你 pad-left。返回另一个包的下载量,比不返回更糟。
youtube-video,数对统计项
这个模板读的是 meta[itemprop="interactionCount"]——观看页上并不存在这个属性,所以对每一个真实视频,播放量都返回 null。真实的标记是每项统计一个 schema.org InteractionCounter 块,彼此只靠同级的 interactionType 区分——而 YouTube 会先输出 LikeAction 计数器,所以最直觉的改法会悄悄把点赞数当成播放量返回。现在改为按 interactionType 选取,并解析为数字,因此零播放量读作 0 而不是 null;模板描述里早就承诺过的 likes 字段也补上了。
PullPush 停摆之后的 Reddit 全站搜索
如果你的 Reddit 流水线这个月开始返回 429,解决办法不是把 sleep 调长、轮换 user agent 或加代理。这三种我们都试过了。现在每一次向 PullPush 发出的请求都会得到:
{ "error": "Rate limit exceeded. This website does not provide free scraping resources for agents..." }换 user agent、不带 user agent、用浏览器的 user agent——都一样;从某些服务器 IP 过去,收到的还会是 Cloudflare 的 403 质询页。这是一项政策,不是速率限制。
它比「一个接口挂了」更要紧,因为 PullPush 承担的角色是独一份的。另一个公开的 Reddit 归档 Arctic Shift,在设计上就无法跨全站做关键词搜索。 向它发出既不指定子版块也不指定作者的查询,它会返回 HTTP 400:
'query' query parameter requires one of: author, subreddit
于是,带范围的搜索(指定某个子版块、某个用户)照常可用,而不带范围的搜索——你知道那句话,但不知道它在哪个社区——彻底没有了后端。偏偏这才是大多数人真正想要的搜索,也是 Reddit 官方 API 从来没有干净地开放过的能力。
现在 reddit_search 分两步完成它。**发现:**一次限定在 reddit.com 的站内网络搜索,按相关性顺序返回帖子 URL,再从 /comments/<id>/ 这一段取出帖子 ID。**填充:**这些 ID 一次性批量提交给 Arctic Shift 归档,取回真正的帖子记录——分数、评论数、子版块、作者、时间戳、完整正文(selftext)——随后把归档自己的排序重新映射回相关性顺序。
关键区别在于:这些是归档记录,不是搜索摘要。 发现阶段只负责提供地址。响应会带上 source: "web_discovery",让你知道是哪条路径服务的。
两个限制,都会明确告知而不是藏起来:after 和 before 无法在发现路径上应用,响应会说明该过滤条件被忽略了,而不是返回一批看起来像是过滤过的结果;不带范围的评论搜索现在完全没有后端,因此它会请求你补上 subreddit 或 author 范围,而不是抛出一个笼统的失败。PullPush 已经不在任何自动路径里,包括作为带范围搜索的兜底——在那个位置上,它能做的只是白白花掉一次请求,再把 Arctic Shift 真正的错误埋在第二次失败底下。显式传入 source: "pullpush" 仍然可以访问它。
任何还在告诉你「PullPush 是唯一能跨子版块搜索的工具」的页面,都写于 2026 年 8 月之前。
真的会触发的变更追踪
本次发布中 track_changes 共有七处缺陷,而第一处意味着价格监控——这个工具的招牌用法——根本不工作。
价格变动是按它在页面上占多大篇幅来打分的。 显著性判断纯粹是体积式的,所以 $19.99 → $29.99 和 $19.99 → $99.99 都被判为「minor」;而在不限定范围时,这个变化压根不会被记录。由于 notificationThreshold 默认是「moderate」,用最直觉的方式建起来的监控从来不会触发。现在金额会被直接比较,其相对幅度会把显著性至少提升到「moderate」,超过 20% 则为「major」。只有带货币标记的数字才计入,所以播放量计数和评论总数不会误触发;千位分隔符可以解析,所以 $1,299 读作 1299。这一对数值会出现在 details.valueChanges 里,让你看清监控为什么触发。
customSelectors 从来没有限定过任何范围。 它只在分节分析里被读取,而在那里它是增加哈希——所以限定范围反而让比较更糟。在一个 Amazon 商品页上,把范围限定到价格区块,修改元素数从 456 涨到 3204,payload 从 5.35 MB 涨到 6.18 MB,而且在价格根本没动的页面上报告了变化。现在分析会先把文档收窄到匹配的子树,于是哈希、相似度和 diff 一起被限定。匹配不到任何元素的选择器会回退到整篇文档并发出警告,而不是悄悄地什么都不追踪。
structuralSimilarity 在两个方向上都在说谎。 在它压根没测量的时候,它报告 0——而 0 是一个真实分数,含义是「结构完全变了」,所以关闭结构追踪反而产生了「结构已彻底改变」这个最强信号。现在未测量时返回 null。然后是 5.2.1:这个分数永远无法低于 0.5,因为求平均的层级那一半比较的是一个初始化为空、之后从未写入的对象,恒定返回 1。一个用相同标签、但嵌套结构完全不同的重建页面,会拿到满分 1.0——而这恰恰是该指标存在的意义所在。现在它是按深度统计元素数量的直方图,用加权 Jaccard 比较。
diff 的 payload 有了上限。 line_diff 会把变更前后的完整文档整个塞进去,因为空白折叠会让 diff 退化成「全部删除、全部新增」。两种 diff 现在都带有明确的 omittedEntries 标记。
在 5.2.5 里,测试 Zillow 和 Newegg 的价格追踪时又发现了三处。
把范围限定到少数几个标签之外的任何标签,就什么也追踪不到。 元素级分析索引的是一份固定白名单——h1-h6、p、div、span、a——所以限定到 address、td、li、tr 或 dd 的监控,建立的基线是零个元素,永远不可能报告元素级变化。一个限定到 ['address'] 的 Zillow 页面,从 9 个匹配节点建出了 0 个元素的基线。现在白名单之外的匹配也会被哈希;白名单内的标签会跳过,因此限定到 div 的计数保持不变。
限定了范围的比较会悄悄按不限范围来跑。 compareWithBaseline 会丢弃调用方的追踪选项,改用基线的。这本身是正确的,也是有效 diff 的必要条件——两侧必须以相同方式分析,而限定过范围的基线不再保留完整文档可供重新限定——但它什么都不说,于是限定范围的比较返回的结果与不限范围时完全相同,你却无从察觉。被忽略的选项现在会通过 warnings 数组返回。
在没有发现任何变化的比较上也会出现「Text content changed」,因为低于阈值的 token 噪声仍会填充 textChanges,哪怕 hasChanges 是 false。摘要现在以显著性为准。
已对暴露出这些问题的真实 Zillow 页面做过验证:基线从 0 个元素变为 9 个;而此前报告 true/「moderate」/28 处修改的比较,现在在 100% 相似度下报告 false/「none」/0 处修改。
抓取保真度:被误删的内容
scrape 的三处缺陷同源:那些用来剥离不可见内容的清理步骤,把可见内容也剥掉了。
框架流式渲染的内容被删除。 隐藏内容清理会移除 <div id="S:0" hidden>,而这正是 Next.js App Router 流式输出渲染结果的地方。在定价页上,那个容器就是整个可见页面,于是 markdown 返回空白,所有价格随之消失。现在容器判定会在文本占比和标记占比之间取较大者。
脚本内容被算作页面正文。 批量移除的保护逻辑用 $('body').text() 来衡量元素大小,而它包含每个内联 <script> 的源码。在一个 Shopify 店铺上,分母是 62,269 个字符,其中只有 4,295 个是可见文案——于是一个装着整个商品区块的容器算下来低于阈值,被连同价格一起删掉;此后 json 路径没有价格可提取,模型就自己编了一个。现在比值的两端都排除 script/style/noscript/template。
LLM 回显的 schema 被当作数据返回。 json 格式只读取 extract_with_llm 的 success 字段而丢弃其余,于是三类失败以「干净提取」的面貌交到调用方手里:返回的是 schema 文档而非页面数据(格式良好的 JSON,且在没有必填字段时连校验也能过)、未通过 schema 校验的输出,以及在 50,000 字符上限处被静默截断的输入。现在 extract_with_llm 会检测 schema 回显、重试一次,并以可据以行动的错误失败;scrape 则把 schema 不匹配和截断作为警告暴露出来,同时保留数据。
以及在 5.2.2 里,markdown 格式不再返回原始 HTML。 turndown-plugin-gfm 只有在表格首行全部是 <th> 时才转换它;其他表格都会落进插件的 keep 过滤器被原样输出。而真实网页里到处都是布局表格——用来排版而非承载数据的表格,Hacker News 整个站就是用它们搭起来的——所以 scrape 配合 formats: ["markdown"]、extract_text 配合 output_format: "markdown" 返回的都是 <table> 标记。现在在插件之后注册的规则会先一步匹配到这些表格,并把它们展平成单元格内容。带有真正表头行的表格不受影响,仍然渲染成 GFM 管道表格;我们拿一张维基百科的数据表做了前后对比,逐字节一致。
本地 LLM:Ollama 终于能用了
Ollama 从来就没有被注册为 LLM 提供方。 LLMManager 只注册了 OpenAI 和 Anthropic,两者都以 API key 为前提。所以在一台装了 Ollama、却没有任何云端密钥的机器上,extract_structured 会完全跳过 LLM 提取并报告 css_fallback——产出诸如 "$79.99$79.99" 这样的值,还会丢字段——而 deep_research 则悄悄关闭了查询扩展、语义排序和综合。extract_with_llm 自带一个私有客户端,这就是它能用、并且掩盖了这个缺口的原因。此外,失败的 LLM 调用也不再报告 extraction_method: "llm" 和 0.9 的置信度。
模型路由会挑选已安装的最佳模型,而不是一律用 llama3.2。以三个真实商品页做基准、并有经过核对的标准答案:gemma3:4b 拿到 18/18,耗时 1040 毫秒,而 llama3.2 是 16/18——并且失败是系统性的,不是采样噪声:五次运行里 llama3.2 五次都编造了划线价。参数量并不能预测准确度,4B 的模型同时赢了一个 12B 和一个 20B。selectOllamaModel() 会选出已安装模型中评级最高的那个;如果你设置了 OLLAMA_DEFAULT_MODEL,它仍然优先。
远程 Ollama 接入点通过 OLLAMA_API_KEY 生效。 当该变量被设置时,每一次 Ollama HTTP 调用都会带上 Authorization: Bearer,所以托管部署可以指向 Ollama Cloud 或任何有鉴权前置的实例,完全不需要 OpenAI 或 Anthropic 的密钥。不设置则一切照旧。
浏览器自动化与 stealth 卡死
scrape_with_actions 带着七处 Playwright API 与错误恢复缺陷,全都对测试套件不可见,因为它的假页面会实现执行器调用的任何东西——包括 Playwright 根本没有的 API:
scroll toElement调用了scrollIntoView(),而 handle 和 locator 上都没有这个方法,所以这个分支每次执行都抛错。wait动作对外宣称支持enabled/disabled/stable,却把它们传给了一个会拒绝这些值的 API。- 每个动作的
Promise.race与它所竞速的工作共用同一个截止时间并总是胜出,于是 Playwright 真正的错误被替换成一句干巴巴的「Action timeout」——并且每个动作都留下一个仍在运行的定时器。 - 点击和按键不会等待它们所替换掉的那个文档。
- 链式重试是在上一次失败留下的状态上重放的,从不重新加载。
- 所有恢复策略都挡在
retries > 0之后,而 schema 把retries默认设成了 0,所以没有一个能被执行。
stealth 浏览器会把自己卡死。 stealth_mode create_page 从不关闭它创建的页面,于是每调用一次就泄漏一个 Chromium 渲染进程,直到实例耗尽内存。卡死的浏览器随后会被「只判断真假」的检查一直复用下去,而清理逻辑又会在对已死浏览器的协议调用上挂起,导致无法远程解除卡死。现在有了 isConnected() 尸体检测、断连处理器、以 5 秒截止时间竞速关闭并以 SIGKILL 兜底、重建连接池的清理流程,以及一个启动中互斥锁。
托管镜像忽略了系统自带的 Chromium。 chromium.launch 现在会遵循 PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH——Dockerfile 一直都设置了它,但 Playwright 自己从不读取环境变量——所以浏览器路径不会再在 Alpine 上因为找一个从未下载过的 chrome-headless-shell 而崩溃。
语言检测与 PDF 表格
五种语言无法被检测。 analyze_content 用一份语言名称映射表过滤 franc 的输出,而那份表里有五个键用的是 ISO 639-2/B 代码——chi、gre、ara、nor、msa——而 franc 输出的是 ISO 639-3,永远不会产生它们。后果彻底且无声:一个通篇中文的页面返回 null;希腊语、阿拉伯语、挪威语和马来语同样如此。现在这些代码已改为 cmn、ell、arb、nob 和 zlm/zsm。
它背后还压着第二个 bug:franc 按出现最多的文字系统打分,而中文或日文页面总会夹带一串英文产品名和代码示例,于是整页被判成拉丁字母的散文。现在,当汉字、假名或谚文占到字母总数的 10% 以上时,检测会直接短路——在真实页面上实测,拉丁文页面是 0%,真正的 CJK 页面在 24% 到 51% 之间。
PDF 表格提取对所有传入的文档都返回 []。 托管的 process_document 路由读取的是 TableResult.mergedTables——这个字段 PDF 库声明了却从不写入;表格其实在 result.pages[].tables 上。用三份真实 PDF 验证——arXiv 的《Attention Is All You Need》、美国国税局的 W-9 和 1040 表——被读取的那个字段三次都是 0 张表,而分页数组里分别有 7、6 和 11 张。除了换字段,还做了几件事:单元格全空的表格会被丢弃(那是画出来的框——表单轮廓和插图边框——不是数据);每张表会报告自己所在的页码;20 张表的上限会说明它省略了多少张;而只有横线、没有竖线的表格(学术论文的惯例)会附带一条说明,而不是被当成真正的单列表格。
基础设施:缓存、传输、Chromium
CACHE_DIR 和 CACHE_ENABLE_DISK 什么也没做。 这两个配置项一直都暴露着,但 CacheManager 一个都没读:目录是写死的,磁盘缓存则无条件开启。于是每个测试进程都共用同一个目录,而这个目录能挺过每一次运行——已经堆积了 1,778 个文件——又因为 crawl_deep 是按被抓取的 URL 做键,而测试服务器绑定的是操作系统会回收复用的临时端口,一次运行有可能拿到上一次运行对另一个站点的抓取结果。这正是并行运行时那个偶发抖动的根因,线索藏在耗时里:两个各自要执行两轮完整抓取的测试,失败时只花了 13 毫秒和 5 毫秒。默认值没有变——这两个变量只是终于做了它们一直声称在做的事。
crawl_deep 的 cacheEnabled: false 只关掉了一半缓存。 BFSCrawler 自建了一份缓存,把每个抓取到的页面正文都存了下来,且无法关闭——所以明确要求不要缓存的调用方,拿到的仍然是缓存页面。两份缓存还都会写磁盘,尽管代码里把它们描述为「按会话、用完即毁」;落到磁盘上之后,页面正文比爬虫多活一个小时,还会跨进程边界。现在两者都只在内存中。
支持任务的工具在 Streamable HTTP 上失败。 cloneServerForSession 丢掉了 capabilities 和 task store,导致对 agent、crawl_deep、batch_scrape 或 deep_research 调用 tools/call 时抛出「No task store provided for task-capable tool.」。
提取器只留一份
MCP 服务器和 CrawlForge REST API 各自带着一份模板提取器的拷贝,用两种语言写成,而且没有任何机制能察觉它们分叉——结果两天里分叉了两次。amazon-product 在 8 月 25 日于 MCP 一侧修好,REST 那份却一直返回 null 评分和 "Brand: Amazon",直到 26 日;而 shopify-product 只存在于其中一侧。
现在只有一份实现,发布为 crawlforge-extractors,两侧都安装它。TemplateRegistry 从该包重新导出,API 不变——对 scrape_template 的调用方而言什么都没变。它是运行时依赖而非 peer dependency,所以 npm install -g crawlforge-mcp-server 会自动带上它,不需要额外安装任何东西。
还有两项行为也搬了过去,因为一侧有、另一侧没有:
readBody会用响应自身声明的字符集解码,并拒绝缓冲超过上限的内容。MCP 服务器两件事都做了;REST API 调用的是response.text(),它假定 UTF-8——所以以 Shift_JIS、GBK 或 ISO-8859-1 编码提供的页面回来就是乱码——并且会把任意大小的响应体缓冲进一个 serverless 函数里。structureSignature用「标签词汇表 + 按深度统计的元素数量直方图」为文档生成指纹,小到可以和变更追踪的基线存在一起;它还接受一个可选的根节点,让追踪某个 CSS 选择器的调用方只对那一块区域打分。
另一条路是写一个对等性测试,事后告诉我们哪一份拷贝错了。删掉第二份拷贝,比检测它的漂移更划算。
credits 成本
本次发布只有一个价格变动。reddit_search 从 2 credits 调整为 5 credits,与 search_web 持平。
原因就是上面那套机制:一次 Reddit 全站搜索如今花掉的,正是 search_web 所花的那一次上游搜索调用,我们的成本相同。带范围的搜索仍然直接查归档,对我们没有额外成本,但同样按 5 credits 计费,因为这个工具只有一个对外公布的价格,而不是一个需要你自己去推算的按请求定价。按 Free 方案一次性发放的 1,000 credits 算,这就是 200 次 Reddit 搜索。
其余全部不变:scrape_template 无论用哪个模板都是每次 1 credit,fetch_url 是 1,track_changes 是 3,crawl_deep 是 4,batch_scrape 是每个 URL 5。失败的请求从不计费。
如何升级
npm install -g crawlforge-mcp-server@latest
crawlforge --version # 5.2.5如果你的 MCP 客户端是用 npx 启动服务器的,下次重启时就会拿到新版本。5.2.5 直接依赖 crawlforge-extractors@^1.2.1,因此 youtube-video 的播放量修复会随之带入。
没有任何破坏性变更:没有工具被改名,没有输出结构变化,只有 reddit_search 调整了价格。升级前唯一值得核对的是预算——如果你在循环里调用 reddit_search,它现在是 5 credits,不是 2。
同样的修复已在托管的 REST API 上线,所以如果你是通过 HTTP 调用 CrawlForge,无需安装任何东西。
想试试这些能力,又不想自己搭服务器? 免费开始,赠送 1,000 credits——无需信用卡——然后浏览 28 个工具的 API 参考。延伸阅读:2026 年仍然可用的 Reddit API 替代方案 和 用本地 LLM 与 Ollama 提取数据。
亲自试一试——无需注册
在 Playground 中运行 CrawlForge 的 28 个抓取与提取工具中的任意一个,然后免费开始,获取 1,000 credits。
1,000 免费 credits • 一次性 • 无需信用卡
标签
及时获取最新洞察
将教程、产品更新与 Web 抓取技巧直接发送到你的收件箱。
拒绝垃圾邮件,随时可取消订阅。