内容
# 爬虫引擎
[](LICENSE)
[](https://nodejs.org/)
[](https://unmaintained.tech/)
> 作为社区开源快照发布,供社区使用和构建。此代码库不主动维护 - 问题和拉取请求不被监控。在MIT许可证下自由分叉。
适用于TypeScript/Node.js的生产级、可嵌入的网络爬虫。将其插入任何需要遍历网站并获取丰富的页面快照流的工具 - SEO审计器、断链检查器、内容监视器、竞争情报工具。
引擎没有UI,没有存储,也没有内置报告。它在爬取时生成`PageSnapshot`记录。
---
## 项目布局
```
crawl-engine/
├── core/ @crawl/engine - 纯粹的爬虫引擎,无界面限制
├── modules/
│ ├── chrome-pipe/ @crawl/chrome-pipe - 使用Chrome作为获取管道,爬虫作为提取大脑
│ ├── mcp-test/ @crawl/mcp-test - 用于烟雾测试的最小化3工具MCP服务器
│ └── mcp-server/ @crawl/mcp-server - 具有爬虫、获取和分析工具的完整MCP服务器
├── configs/
│ └── examples/ 参考配置(全站审计、内容发现、本地SEO)
├── scratch/ 文件输出沙盒(gitignored)
```
**依赖规则:** `core`不导入此仓库的任何内容。`modules`仅导入`core`。`configs`是数据 - 没有导入。箭头始终指向内部。
---
## 快速开始
**要求:** Node.js 20+
### 开发容器(推荐用于任何操作系统)
仓库中包含一个`.devcontainer/`配置,用于VS Code和Cursor。在开发容器中打开仓库,为您提供一个带有Node 20、原生模块编译和Playwright预安装的Linux环境 - 无需本地设置。
1. 安装 [Docker Desktop](https://www.docker.com/products/docker-desktop/)
2. 在VS Code / Cursor中:**在容器中重新打开** (或从命令面板中的`Dev Containers:Clone Repository in Container Volume`)
3. `npm install`和`npx playwright install`在首次构建时自动运行
端口3001和3002自动转发到localhost。
### Windows设置(在开发容器外,一次性)
```bash
# 允许深node_modules路径(Windows默认路径长度限制为260个字符)
git config core.longpaths true
# 规范文件模式 - Windows没有Unix执行位,因此如果没有此设置,每次Mac / Linux协作者触摸仓库后,每个文件都会出现修改
git config core.fileMode false
```
行尾通过`.gitattributes`自动处理(仓库中使用LF,Windows上检出时使用CRLF)。您不需要手动设置`core.autocrlf`。
### 安装
```bash
npm install # 从根目录安装所有工作区
```
### 构建
```bash
npm run build -w core
npm run build -w modules/mcp-server
```
或一次性构建所有内容:
```bash
npm run build --workspaces --if-present
```
### 测试
```bash
npm run test -w core
```
---
## 核心引擎(`core/`)
### 程序化使用
```ts
import {
CrawlConfig,
CrawlEngine,
HttpClientBackend,
SsrfPolicy,
} from '@crawl/engine';
// 使用HttpClientBackend进行标准站点,或使用PlaywrightFetchBackend进行JS渲染的SPA
const backend = new HttpClientBackend(SsrfPolicy.BLOCK_PRIVATE);
const config = CrawlConfig.builder('https://example.com')
.maxDepth(3)
.workers(8)
.requestDelayMs(300)
.build();
for await (const page of new CrawlEngine(config, backend, []).crawl()) {
console.log(page.statusCode, page.url);
}
```
### 使用模式缩小范围
```ts
const config = CrawlConfig.builder('https://example.com/blog')
.maxDepth(5)
.includePattern('/blog/') // 仅跟随包含/blog/的URL
.excludePattern('/tag/') // 跳过标签存档页面
.excludePattern('?page=') // 跳过分页
.requestDelayMs(500)
.jitterPct(20) // ±20%随机化每个请求
.build();
```
### 自定义提取器
```ts
import type { Extractor, ParsedPage } from '@crawl/engine';
interface SchemaSignals {
jsonLdBlocks: string[];
hasProductSchema: boolean;
}
class SchemaExtractor implements Extractor<SchemaSignals> {
readonly id = 'schema.jsonld';
extract(page: ParsedPage): SchemaSignals | null {
if (!page.isHtml) return null;
const blocks = page.$('script[type="application/ld+json"]')
.map((_, el) => page.$(el).html() ?? '')
.get();
return {
jsonLdBlocks: blocks,
hasProductSchema: blocks.some(b => b.includes('"Product"')),
};
}
}
const engine = new CrawlEngine(config, backend, [new SchemaExtractor()]);
for await (const page of engine.crawl()) {
const signals = page.extraction<SchemaSignals>('schema.jsonld');
if (signals?.hasProductSchema) console.log('产品架构:', page.url);
}
```
### CrawlConfig参考
所有设置都具有适合礼貌、作用域感知的爬取的安全默认值。通过`CrawlConfig.builder(seedUrl)`构造 - 所有验证都在`build()`时进行。
#### 范围
| 构建方法 | 默认值 | 描述 |
|---|---|---|
| `maxDepth(n)` | `Infinity` | 从种子开始的最大链接跳跃次数。`0` = 仅种子。 |
| `crawlSubdomains(bool)` | `false` | 跟随链接到子域。 |
| `includePattern(str)` | _(无)_ | 仅爬取包含此子字符串的URL。多个调用是OR-ed。 |
| `excludePattern(str)` | _(无)_ | 跳过包含此子字符串的URL。优先于包含。 |
| `checkExternalLinks(bool)` | `false` | HEAD检查外部链接;将其状态包含在快照中。 |
#### 并发
| 构建方法 | 默认值 | 描述 |
|---|---|---|
| `workers(n)` | `4` | 并发获取承诺的数量。 |
#### HTTP / 传输
| 构建方法 | 默认值 | 描述 |
|---|---|---|
| `timeoutMs(ms)` | `10000` | 每个请求的HTTP超时时间。 |
| `maxBodyBytes(n)` | `5242880`(5 MB) | 响应正文上限。较大的正文被截断;仍然发出快照。 |
| `maxRedirects(n)` | `10` | 每个URL的最大重定向跳跃次数。 |
| `userAgent(str)` | Chrome UA | 发送每个请求的User-agent字符串。 |
| `renderJs(bool)` | `false` | 启用JS渲染(需要`PlaywrightFetchBackend`)。 |
| `postNavigationDelayMs(ms)` | `0` | 页面加载后等待捕获DOM的时间(适用于SPA)。 |
#### 流量整形
| 构建方法 | 默认值 | 描述 |
|---|---|---|
| `requestDelayMs(ms)` | `500` | 相同主机请求的基准延迟。`0`禁用。 |
| `jitterPct(pct)` | `20` | ±%随机化应用于每个延迟。 |
| `retryDelayMs(ms)` | `2000` | 429重试的基准回退(每次尝试加倍)。 |
| `maxRetries(n)` | `5` | 放弃URL之前的最大429重试次数。 |
#### 网络标准
| 构建方法 | 默认值 | 描述 |
|---|---|---|
| `respectRobotsTxt(bool)` | `true` | 为每个域获取和尊重`robots.txt`。 |
| `seedFromSitemap(bool)` | `true` | 从`sitemap.xml`预加载前沿。 |
| `detectDuplicates(bool)` | `true` | 标记与其先前正文匹配的页面。 |
| `stripSessionParams(bool)` | `true` | 从URL中删除`PHPSESSID`、`JSESSIONID`等。 |
#### 安全
| 构建方法 | 默认值 | 描述 |
|---|---|---|
| `ssrfPolicy(policy)` | `BLOCK_PRIVATE` | 阻止请求到私有/环回IP地址范围。 |
---
## MCP服务器(`modules/mcp-server/`)
将爬虫引擎暴露为Claude和任何MCP兼容客户端的工具。
### 启动
```bash
node modules/mcp-server/dist/index.js
```
### 工具
| 工具 | 描述 |
|---|---|
| `crawl` | 从种子URL爬取站点。返回具有SEO和链接数据的页面快照清单。 |
| `fetch_page` | 获取单个URL并返回其SEO数据、文章内容和链接。 |
| `fetch_api` | 遍历偏移分页的JSON API并返回收集的结果。 |
| `parse_sitemap` | 解析`sitemap.xml`并返回所有发现的URL。 |
| `search_manifest` | 按关键词、URL模式或状态代码搜索保存的爬取清单。 |
| `summarize_manifest` | 返回保存的清单的聚合统计数据(页面数量、字数、状态分布)。 |
| `analyze_links` | 分析清单的链接图 - 内部、外部、损坏。 |
| `analyze_meta` | 从清单中提取规范、机器人、开放图和hreflang信号。 |
| `analyze_headings` | 审核清单的标题结构(H1-H6)以查找SEO问题。 |
| `analyze_images` | 审核图像是否存在缺少的替代文本和其他可访问性信号。 |
| `analyze_schema` | 提取并总结清单上的JSON-LD架构块。 |
| `compare_manifests` | 差异两个爬取清单以显示新的、已删除的和已更改的页面。 |
| `find_orphans` | 确定没有内部传入链接的页面。 |
### 提取器
MCP服务器自带两个内置提取器,注册在每个爬取中:
- **`SeoExtractor`** - 标题、元描述、H1、字数、文章字数、摘要
- **`LinkExtractor`** - 具有锚文本的内部和外部链接
---
## Chrome管道(`modules/chrome-pipe/`)
一个示例MCP模块,它颠倒了通常的架构:与其让爬虫获取页面,Claude在Chrome中导航并提供原始HTML,而该模块处理所有提取。不会发出出站HTTP请求 - 它是一个纯粹的提取层。
此模式在需要真实浏览器会话(用于JS渲染的页面、身份验证内容或机器人保护的站点)但仍需要结构化提取输出而不是原始HTML时很有用。
### 工作原理
```
Chrome中的Claude chrome-pipe MCP服务器
│ │
│ 导航到URL │
│ get_page_text() → html │
│ ─────────────────────────── >│
│ extract_page(url, html)
│ │
│ < ────────────────────────── │
│ 页面快照(SEO、链接、 │
│ 标题、架构、开放图) │
```
### 工具
| 工具 | 描述 |
|---|---|
| `extract_page` | 在URL + HTML字符串上运行完整的提取器堆栈。返回SEO信号、链接、标题、JSON-LD架构和开放图数据。 |
| `extract_seo` | 仅限SEO信号 - 标题、描述、规范、机器人、H1、字数、文章字数、摘要。 |
| `extract_links` | 具有锚文本和rel属性的内部和外部链接。 |
| `batch_extract` | 一次运行最多100个页面的提取。返回一个清单数组。 |
### Claude Desktop配置
```json
{
"mcpServers": {
"chrome-pipe": {
"command": "node",
"args": ["modules/chrome-pipe/dist/index.js"]
}
}
}
```
### 示例Claude工作流
> “导航到https://example.com,获取页面HTML,然后将其传递给chrome-pipe的`extract_page`工具,并向我显示SEO信号和标题结构。”
Chrome中的Claude处理获取。Chrome-pipe处理提取。两个MCP服务器之间直接不进行通信 - Claude是协调器。
---
## 架构
```
CrawlEngine.crawl() AsyncIterable<PageSnapshot>
│
├── 种子前沿(站点地图+种子URL)
│
└── 分派循环
承诺池(≤工作并发任务)
每个工作:
机器人检查
速率限制(每主机承诺链)
FetchBackend.fetch()
└── SsrfGuard.check() 阻止私有IP地址范围
BodyDeduplicator.isDuplicate()
cheerio.parse() 仅限HTML
Extractor[].extract() 用户注册的提取器
发出PageSnapshot
提取链接→ Frontier.submit()
```
### 并发模型
引擎使用显式承诺池 - 调度循环最多维护`workers`并发承诺,并使用`Promise.race`等待池满。这产生了与每个任务的线程模型相同的吞吐量,用于I/O绑定的爬取。
### 速率限制(每主机)
每个工作器都链接到前一个主机承诺,将请求序列化到同一主机,延迟配置,无需原子操作 - Node.js的单线程事件循环保证非交错的等待点。
### 429回退
在HTTP 429时,工作器在睡眠后重新获取池槽,因此长时间回退不会使其他正在进行的URL资源不足。
---
## 安全
### SSRF保护
在连接之前,每个URL的主机名被解析为IP并检查阻止的范围:
| 范围 | 描述 |
|---|---|
| `10.0.0.0/8`,`172.16.0.0/12`,`192.168.0.0/16` | RFC 1918私有 |
| `127.0.0.0/8` | 环回 |
| `169.254.0.0/16` | 链接本地 / AWS实例元数据 |
| `100.64.0.0/10` | 共享地址空间(RFC 6598) |
| `::1/128`,`fc00::/7`,`fe80::/10` | IPv6环回 / 唯一本地 / 链接本地 |
检查在重定向链的每个跳转处触发。要为内网爬取禁用:
```ts
CrawlConfig.builder('http://intranet.corp/').ssrfPolicy(SsrfPolicy.ALLOW_ALL).build();
```
*注意:基于Playwright的爬取(使用`PlaywrightFetchBackend`)也为所有传出的请求和子资源强制执行相同的SSRF策略。*
### 路径遍历和沙盒
用户提供的用于文件写入的路径(例如,MCP工具中的`saveToFile`)通过`Security.sandboxPath()`处理。这确保:
- 默认情况下,操作仅限于`./scratch/`目录。
- 阻止绝对路径和遍历组件(`..`)逃离沙盒。
### 原型污染缓解
所有从不受信任的HTML分配动态键的提取器(开放图、Twitter卡、架构块)都显式过滤掉`__proto__`、`constructor`和`prototype`以防止环境操作。
### 正文大小限制
响应正文上限为`maxBodyBytes`(默认5 MB)。截断的正文仍会产生快照 - `fetchResult.bodyTruncated`为`true`。
### 重定向深度限制
重定向链上限为`maxRedirects`(默认10)。较长的链被视为错误。
---
## URL标准化
在将任何URL加入队列之前,`UrlNormalizer`按顺序应用这些规则:
1. 拒绝非http / https或格式错误的URL
2. 小写方案和主机
3. 删除默认端口(http上的`:80`,https上的`:443`)
4. 解析点段
5. 删除片段(`#...`)
6. 按字母顺序对查询参数进行排序
7. 删除跟踪参数:`utm_*`、`fbclid`、`gclid`、`gclsrc`、`dclid`、`zanpid`、`mc_cid`、`mc_eid`
8. 如果`stripSessionParams=true`,则删除会话参数:`jsessionid`、`phpsessid`、`sid`、`sessionid`
9. 非根、无扩展路径删除尾部斜杠
标准化是幂等的。
---
## 依赖项
### `core/`运行时
| 包 | 许可证 | 目的 |
|---|---|---|
| `cheerio` | MIT | HTML解析 |
| `robots-parser` | MIT | robots.txt解析 |
| `undici` | MIT | HTTP客户端 |
| `playwright-core` | Apache 2.0 | JS渲染的无头浏览器 |
### `modules/mcp-server/`运行时(添加`core`)
| 包 | 许可证 | 目的 |
|---|---|---|
| `@modelcontextprotocol/sdk` | MIT | MCP服务器协议 |
| `@mozilla/readability` | Apache 2.0 | 文章提取 |
| `got-scraping` | MIT | 用于机器人绕过的TLS指纹伪装 |
| `jsdom` | MIT | 可读性DOM模拟 |
| `express` | MIT | MCP的HTTP传输 |
| `zod` | MIT | 模式验证 |
### `modules/chrome-pipe/`运行时(独立)
| 包 | 许可证 | 目的 |
|---|---|---|
| `@modelcontextprotocol/sdk` | MIT | MCP服务器协议 |
| `@mozilla/readability` | Apache 2.0 | 文章提取 |
| `cheerio` | MIT | HTML解析 |
| `jsdom` | MIT | 可读性DOM模拟 |
| `zod` | MIT | 模式验证 |
所有依赖项都是允许的开源(MIT或Apache 2.0)。所有逻辑 - 前沿管理、SSRF防护、URL规范化、提取器框架和MCP工具定义 - 是此仓库中的原始代码。
---
## 许可证
MIT © Timothy Nishimura. See [LICENSE](LICENSE).
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器