optimiweb/kumo
GitHub: optimiweb/kumo
Kumo 是一个 fail-closed 的 Go 网络爬虫库,专为生产环境中租户隔离的受控抓取而设计,内置安全默认值和任务生命周期管理。
Stars: 2 | Forks: 0
# Kumo
Kumo 是一个 fail-closed 的 Go 库,专为生产环境的网络爬虫设计。
它负责处理安全的 HTTP 抓取、任务生命周期(claim、lease、settle)、robots.txt
以及重定向处理。你的应用程序只需提供 handler、host policy,以及可选的
用于分布式爬取的持久化队列 adapter。
```
go get github.com/optimiweb/kumo
```
## 功能
- **类型化的 handler** — 不可变的输入,显式的 `Ack` / `Retry` / `Fail`
- **两种运行模式**
- `RunDirect` — 有界限的本地爬取
- `RunFrontier` — 队列支持的爬取(内存或你自己的 adapter)
- **受控的出口流量** — 仅支持 GET/HEAD,无代理或 cookie,拒绝私有 IP
- **默认启用 Robots** — 通过相同的 pipeline 获取并执行
- **手动重定向** — 每一跳都是独立 claim 的任务
- **有界限的 body** — 网络传输和解码大小受限;不进行静默截断
- **内置内存 adapter** — 无需数据库驱动即可启动
## 用法
### Direct 模式
最适合一次性或有界限的小型爬取:
```
package main
import (
"context"
"fmt"
"time"
"github.com/optimiweb/kumo"
"github.com/optimiweb/kumo/memory"
)
func main() {
cfg := kumo.DefaultCollectorConfig()
cfg.Policy = kumo.HostPolicy("example.com")
c, err := kumo.NewCollector(cfg)
if err != nil {
panic(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
report, err := c.RunDirect(ctx, kumo.DirectRunConfig{
Seeds: []string{"https://example.com/"},
Storage: memory.NewMemoryStorage(nil),
Identifier: kumo.IdentityFunc(kumo.DefaultIdentity),
MaxWorkItems: 50,
MaxFetchAttempts: 100,
MaxConcurrency: 4,
MaxAttempts: 2,
Handler: kumo.HandlerFunc(func(ctx context.Context, in kumo.HandleInput, sink kumo.DiscoverySink) kumo.Decision {
res := in.Result()
fmt.Println(in.Lease().Work().URL(), res.Outcome())
if res.Outcome() != kumo.FetchOutcomeHTTPResponse {
return kumo.Fail(res.ErrorCode())
}
// Discover more URLs with sink.Submit(...)
return kumo.Ack()
}),
})
if err != nil {
panic(err)
}
fmt.Printf("handled=%d failed=%d stop=%s\n",
report.Handled(), report.Failed(), report.StopReason())
}
```
### Frontier 模式
当你需要显式队列时使用(此处为内存模式;在持久化/分布式爬取时可替换为你自己的
`Frontier`):
```
cfg := kumo.DefaultCollectorConfig()
cfg.Policy = kumo.HostPolicy("example.com")
c, err := kumo.NewCollector(cfg)
if err != nil {
panic(err)
}
fr := memory.NewMemoryFrontier(memory.MemoryFrontierOptions{
MaxPages: 100, FetchBudget: 200, MaxAttempts: 3, MaxOriginConc: 4,
})
id := kumo.IdentityFunc(kumo.DefaultIdentity)
seed, err := id(ctx, kumo.IdentityRequest{
RawURL: "https://example.com/",
Method: kumo.MethodGET,
Source: kumo.SourceSeed,
})
if err != nil || seed.State != kumo.IdentityAccepted {
panic(err)
}
if _, err := fr.EnqueueSeed(ctx, kumo.EnqueueRequest{
Identity: seed.Identity, Method: kumo.MethodGET,
Source: kumo.SourceSeed, ResourceClass: kumo.ResourceHTML,
}); err != nil {
panic(err)
}
if err := fr.SealSeeds(ctx); err != nil {
panic(err)
}
report, err := c.RunFrontier(ctx, kumo.FrontierRunConfig{
Frontier: fr,
Identifier: id,
Handler: handler,
UntilDrained: true,
PollInterval: 50 * time.Millisecond,
})
```
完整程序请参见 `examples/direct` 和 `examples/frontier-memory`。
### Handler
Handler 会接收一个完成的抓取结果,并返回一个决策:
| 决策 | 含义 |
|---|---|
| `kumo.Ack()` | 成功 — settle 任务 |
| `kumo.Retry(after, code)` | 临时故障 — 安排重试 |
| `kumo.Fail(code)` | 终极失败 |
| `kumo.DefaultDecision(res)` | 自动映射常见的抓取结果 |
在 handler 中通过 `sink.Submit` 提交相关 URL。Kumo 不会
自动提取 HTML 链接;发现逻辑由应用程序自定义。
### CLI
面向 Sitemap 的域名爬取(内存模式):
```
go run ./cmd/kumo example.com
go run ./cmd/kumo -workers 8 -max-pages 1000 example.com
```
## 架构
```
your app / cmd/kumo / examples
│
▼
kumo public facade (NewCollector, RunDirect, RunFrontier)
│
├──► memory in-memory Frontier + DirectStorage
│
▼
internal/engine ──────► crawl contracts (work, handlers, ports, config)
│
▼
internal/httpx controlled single-hop HTTP
```
| Package | 作用 |
|---|---|
| `kumo` | Facade 和默认设置 |
| `crawl` | 稳定的协议:work、lease、handler、`Frontier`、policy、结果 |
| `memory` | 参考的内存 adapter |
| `internal/engine` | Worker、抓取 pipeline、robots、重定向、结算 |
| `internal/httpx` | 安全拨号、地址 policy、有界限的 body |
| `crawltest` | Adapter 一致性辅助工具 |
| `pkg/*` | 独立辅助工具(URL、robots、sitemap、HTML 查询等) |
**库与应用程序的责任划分**
| Kumo 负责 | 你的应用负责 |
|---|---|
| 安全的抓取 pipeline | 持久化存储/队列(可选) |
| Work 的 claim / lease / settle | 超出默认设置的 host 和产品 policy |
| Robots + 重定向编排 | 链接/sitemap 发现逻辑 |
| 内存 adapter | 证据、库存、多租户认证 |
实现 `crawl.Frontier`(及相关 port)即可使用 Postgres、
Redis 或任何其他存储来支持爬虫。`memory` 是参考实现。
## 安全默认值
| 关注点 | 默认值 |
|---|---|
| 方法 | GET, HEAD |
| 协议 | http, https |
| 端口 | 80, 443 |
| Robots | 开启 |
| 重定向 | 不自动跟随 |
| Cookie / 缓存 / 代理 | 关闭 |
| 私有 / 回环 IP | 拒绝 |
对于真实目标,请务必设置 host policy(`kumo.HostPolicy("example.com")`)。
## 开发
```
make check # fmt, vet, modules, tests, build
make test-integration # HTTP fixture crawls (-race)
make coverage-html
make help
```
`test/integration` 下的集成测试会爬取一个内置的
`example.com` 模拟环境(`test/fixtures/example.com`)。
## 许可证
[MIT](LICENSE)
标签:EVTX分析, Go, Robots协议, Ruby工具, 开发组件库, 日志审计, 网络请求