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工具, 开发组件库, 日志审计, 网络请求