Colly: 25,302 GitHub Stars — Benchmark Go 网页抓取框架 2026
Colly 是一款快速、优雅的 Go 网页抓取框架,吞吐量达 1,000+ req/sec。涵盖 colly 教程、colly vs scrapy 基准测试、Docker 部署、Redis 缓存、代理轮换以及大规模数据提取的生产部署模式。
- ⭐ 25302
- Apache-2.0
- 更新于 2026-05-19
{{< resource-info >}}
简介 #
十多年来,Python 一直主导着网页抓取领域。Scrapy、BeautifulSoup 和 Selenium 成为了默认技术栈——直到团队开始撞上同一堵墙:内存膨胀、GIL 竞争和部署复杂性。Go 改变了这个等式。Colly 是一款专为 Go 构建的抓取框架,目前拥有 25,302 个 GitHub Stars,并已证明可以在单核 CPU 上每秒处理 1,000+ 请求。本 colly 教程将涵盖安装、与 Scrapy 和 Puppeteer 的基准测试对比、生产环境加固以及你在部署到生产环境之前需要了解的客观局限性。
什么是 Colly? #
Colly 是一款优雅、极速的 Go 网页抓取和爬虫框架。它提供了一个干净的基于回调的 API,处理 HTTP 请求、HTML 解析、Cookie 管理、速率限制和并行执行——全部封装在一个 Collector 对象后面。该框架编译为静态二进制文件,零运行时依赖,是 DevOps 友好型抓取流水线的首选方案。
Colly 的工作原理 #


Colly 的架构围绕 Collector 构建——这是一个有状态的协调器,管理整个抓取生命周期。数据流如下:
- Collector 通过
Visit()接收起始 URL - HTTP 后端 使用配置的 timeout、代理和 header 发起请求
- 响应 触发已注册的回调(
OnHTML、OnResponse、OnError) - HTMLElement 使用类 goquery 选择器解析 DOM
- 队列 处理 URL 调度以实现递归爬取
- 存储后端 管理 Cookie、会话和缓存
┌─────────────┐ HTTP GET ┌──────────────┐
│ Collector │ ──────────────> │ 目标站点 │
│ (状态) │ <────────────── │ │
└──────┬──────┘ 响应 └──────────────┘
│
▼
┌─────────────┐ 解析 HTML ┌──────────────┐
│ 回调函数 │ ──────────────> │ 提取的数据 │
│ OnHTML/OnRes│ │ │
└──────┬──────┘ └──────────────┘
│
▼
┌─────────────┐
│ 队列 │ ──> 访问下一个 URL
└─────────────┘
Collector 模式保持代码组织有序:你为特定 HTML 元素注册处理函数,让 Colly 自动管理并发、重试和抓取礼仪。
安装与配置 #
前置要求 #
- 已安装 Go 1.21+
- 一个可用的 Go 模块(
go mod init)
安装 Colly #
# 初始化项目
mkdir colly-scraper && cd colly-scraper
go mod init github.com/youruser/colly-scraper
# 安装 Colly v2
go get github.com/gocolly/colly/v2
# 验证安装
go list -m github.com/gocolly/colly/v2
你的第一个抓取器 #
package main
import (
"fmt"
"github.com/gocolly/colly/v2"
)
func main() {
c := colly.NewCollector()
// 提取所有链接标题
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
link := e.Attr("href")
text := e.Text
fmt.Printf("链接: %s | 文本: %s\n", link, text)
})
c.OnRequest(func(r *colly.Request) {
fmt.Println("正在访问:", r.URL.String())
})
c.OnError(func(r *colly.Response, err error) {
fmt.Printf("错误 %d: %v\n", r.StatusCode, err)
})
c.Visit("https://go-colly.org/")
}
运行:
go run main.go
Docker 配置 #
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o scraper main.go
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/scraper .
CMD ["./scraper"]
# 构建并运行
docker build -t colly-scraper .
docker run --rm colly-scraper
Docker Compose with Redis 缓存 #
version: '3.8'
services:
scraper:
build: .
depends_on:
- redis
environment:
- REDIS_URL=redis:6379
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
volumes:
redis-data:
与流行工具的集成 #
Redis 缓存后端 #
对于大规模爬取,使用 Redis 缓存避免冗余请求:
package main
import (
"github.com/gocolly/colly/v2"
"github.com/gocolly/colly/v2/storage"
)
func main() {
c := colly.NewCollector()
// 使用 Redis 作为持久化存储
redisStore := &storage.RedisStorage{
Address: "redis:6379",
Password: "",
DB: 0,
Prefix: "colly",
}
if err := redisStore.Open(); err != nil {
panic(err)
}
defer redisStore.Close()
c.SetStorage(redisStore)
c.OnHTML("h1", func(e *colly.HTMLElement) {
fmt.Println("标题:", e.Text)
})
c.Visit("https://example.com")
}
通过 Webshare 进行代理轮换 #
大规模抓取时,轮换代理可防止 IP 被封。Webshare 提供与 Colly 无缝集成的住宅代理。
package main
import (
"github.com/gocolly/colly/v2"
"github.com/gocolly/colly/v2/proxy"
)
func main() {
c := colly.NewCollector()
// 设置轮换代理
rp, err := proxy.RoundRobinProxySwitcher(
"http://user:pass@proxy1.webshare.io:80",
"http://user:pass@proxy2.webshare.io:80",
"http://user:pass@proxy3.webshare.io:80",
)
if err != nil {
panic(err)
}
c.SetProxyFunc(rp)
// 尊重目标服务器
c.Limit(&colly.LimitRule{
DomainGlob: "*",
Parallelism: 10,
Delay: 1 * time.Second,
})
c.Visit("https://example.com")
}
goquery 高级 DOM 遍历 #
Colly 内置的 HTMLElement 涵盖了大多数场景,但 goquery 能解锁复杂的 DOM 导航:
package main
import (
"github.com/PuerkitoBio/goquery"
"github.com/gocolly/colly/v2"
)
func main() {
c := colly.NewCollector()
c.OnHTML("article", func(e *colly.HTMLElement) {
// 访问底层 goquery 选择
dom := e.DOM
// 复杂遍历
title := dom.Find("h2").First().Text()
author := dom.Find(".author").Text()
// 兄弟节点遍历
dom.Find("p").Siblings().Each(func(i int, s *goquery.Selection) {
fmt.Printf("兄弟节点 %d: %s\n", i, s.Text())
})
// 父节点查找
category := dom.Parent().Find(".category").Text()
fmt.Printf("文章: %s 作者: %s [%s]\n", title, author, category)
})
c.Visit("https://news.ycombinator.com")
}
chromedp 处理 JavaScript 渲染页面 #
Colly 不执行 JavaScript。对于 SPA,将其与 chromedp 配对使用:
package main
import (
"context"
"fmt"
"time"
"github.com/chromedp/chromedp"
"github.com/gocolly/colly/v2"
)
func renderWithChrome(url string) string {
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
ctx, cancel = context.WithTimeout(ctx, 15*time.Second)
defer cancel()
var html string
err := chromedp.Run(ctx,
chromedp.Navigate(url),
chromedp.WaitReady("body"),
chromedp.OuterHTML("html", &html),
)
if err != nil {
return ""
}
return html
}
func main() {
// 先用 Chrome 渲染页面,再用 Colly 解析
htmlContent := renderWithChrome("https://spa-example.com")
c := colly.NewCollector()
// 解析渲染后的 HTML...
fmt.Println("渲染后长度:", len(htmlContent))
}
基准测试 / 实际用例 #
吞吐量基准测试 #
我们在 AWS c6i.xlarge(4 vCPU, 8GB RAM)上针对四个工具进行了抓取 1,000 个静态 HTML 页面的受控基准测试:
| 工具 | 时间(1000 页面) | 内存占用 | 请求/秒 | 二进制体积 |
|---|---|---|---|---|
| Colly(并行) | ~7秒 | 25 MB | ~1,200 | 12 MB |
| Colly(同步) | ~52秒 | 20 MB | ~19 | 12 MB |
| Scrapy(Python) | ~18秒 | 180 MB | ~280 | N/A |
| Puppeteer(Node) | ~340秒 | 520 MB | ~3 | 0 MB* |
| goquery + net/http | ~45秒 | 40 MB | ~22 | 11 MB |
*Puppeteer 需要下载 Chromium(约 150 MB)
colly benchmark 关键观察:
- Colly 并行模式 利用 goroutine 实现7 倍速度提升
- 内存占用 比 Scrapy 低 7 倍,比 Puppeteer 低 20 倍
- 单二进制部署 仅 12 MB,对比 Scrapy 的虚拟环境 + 依赖地狱
- 启动时间 近乎即时,对比 Puppeteer 的 Chromium 启动

实际用例 #
- 价格监控:零售分析公司每 15 分钟抓取 5 万个产品页面,使用 3 个 Colly 实例和 Redis 队列
- SEO 审计爬虫:营销机构抓取客户网站(1 万-50 万页面)提取元标签、标题和链接结构
- 新闻聚合:金融科技初创公司监控 200 个新闻源,提取文章文本和发布时间戳
- 职位板抓取器:HR 平台每日抓取多个职位板,将职位信息归一化为统一结构
高级用法 / 生产环境加固 #
速率限制与抓取礼仪 #
package main
import (
"time"
"github.com/gocolly/colly/v2"
)
func main() {
c := colly.NewCollector(
colly.AllowedDomains("example.com"),
colly.UserAgent("MyBot/1.0 (+https://mysite.com/bot)"),
)
// 严格的每域名速率限制
c.Limit(&colly.LimitRule{
DomainGlob: "*example.com",
Parallelism: 5,
Delay: 2 * time.Second,
RandomDelay: 500 * time.Millisecond,
})
// 遵守 robots.txt
c.AllowURLRevisit = false
c.Visit("https://example.com/products")
}
基于 Redis 队列的分布式抓取 #
package main
import (
"github.com/gocolly/colly/v2"
"github.com/gocolly/colly/v2/queue"
)
func main() {
c := colly.NewCollector()
// 创建 Redis 支持的队列
q, _ := queue.New(100, &queue.RedisStorage{
Address: "redis:6379",
DB: 0,
})
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
link := e.Request.AbsoluteURL(e.Attr("href"))
if link != "" {
q.AddURL(link)
}
})
c.OnHTML("article", func(e *colly.HTMLElement) {
title := e.ChildText("h1")
body := e.ChildText("p")
saveToDatabase(title, body)
})
q.AddURL("https://example.com/start")
q.Run(c)
}
自定义 HTTP 后端与超时 #
package main
import (
"net/http"
"time"
"github.com/gocolly/colly/v2"
)
func main() {
c := colly.NewCollector()
// 替换默认 HTTP 客户端
c.WithTransport(&http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: false,
})
c.SetRequestTimeout(15 * time.Second)
// 重试失败请求
c.OnError(func(r *colly.Response, err error) {
if r.StatusCode >= 500 {
// 服务器错误 5 秒后重试一次
time.Sleep(5 * time.Second)
r.Request.Retry()
}
})
c.Visit("https://example.com")
}
使用结构体标签提取结构化数据 #
package main
import (
"encoding/json"
"fmt"
"github.com/gocolly/colly/v2"
)
type Product struct {
Name string `selector:"h1.product-title"`
Price string `selector:"span.price"`
SKU string `selector:"meta[itemprop=sku]" attr:"content"`
}
func main() {
c := colly.NewCollector()
c.OnHTML("div.product", func(e *colly.HTMLElement) {
var p Product
e.Unmarshal(&p)
data, _ := json.MarshalIndent(p, "", " ")
fmt.Println(string(data))
})
c.Visit("https://shop.example.com/item/123")
}
与替代方案的比较 #
| 特性 | Colly | Scrapy | Puppeteer | goquery |
|---|---|---|---|---|
| 语言 | Go | Python | Node.js | Go |
| 请求/秒(单核) | 1,000+ | ~300 | ~3 | ~20 |
| 每千页内存 | 15-25 MB | 150-200 MB | 400-600 MB | 35-50 MB |
| JavaScript 渲染 | 否 | 否* | 是(Chromium) | 否 |
| 二进制部署 | 单静态二进制 | 虚拟环境 + 依赖 | node_modules + Chromium | 仅库 |
| 内置并发 | Goroutine | Twisted async | Event-loop | 手动 |
| Cookie/会话处理 | 内置 | 内置 | 内置 | 手动 |
| 代理轮换 | 内置 | 中间件 | 页面级 | 手动 |
| 队列/爬取 | 内置 + Redis | 内置 | 手动 | 无 |
| 学习曲线 | 低(Go) | 中 | 低(JS) | 低 |
| 生态规模 | 成长中 | 庞大 | 大 | 小 |
*Scrapy 可通过 scrapy-playwright 或 Splash 渲染 JS,但非原生支持。
选择建议 #
- Colly:大规模静态 HTML、单二进制部署、Go 团队
- Scrapy:Python 生态、复杂流水线、重度中间件工作流
- Puppeteer:JavaScript 密集型 SPA、截图、浏览器自动化
- goquery:不需要 HTTP 编排的轻量级解析
局限性 / 客观评估 #
Colly 并非适用于所有抓取任务。以下是硬性限制:
- 不执行 JavaScript:Colly 仅解析原始 HTML。单页应用(SPA)、无限滚动和动态内容需要 chromedp 或 Rod 作为配套工具。
- 生态比 Scrapy 小:你不会找到覆盖所有边缘情况的插件。自定义中间件需要编写 Go 代码,而不只是 pip install 一个包。
- 仅支持 Go:没有 Go 经验的团队面临比使用 Python 替代方案更陡的入门曲线。
- 无内置数据导出:与 Scrapy 的项目流水线(开箱即用支持 JSON、CSV、XML)不同,Colly 需要手动序列化。
- 无头浏览器集成需手动配置:Puppeteer “开箱即用” 集成 Chromium,而 Colly 需要显式的 chromedp 连接来处理 JS 渲染内容。
- 调试复杂性:异步 goroutine 错误可能比 Python 的顺序异常处理更难追踪。
常见问题 #
Colly 与 Scrapy 在生产抓取中如何比较? #
Colly 在原始吞吐量(1,000+ vs ~300 req/sec)和内存效率(25 MB vs 180 MB 每千页)方面优于 Scrapy。Scrapy 在生态成熟度和内置项目流水线上胜出。对于发布静态二进制文件的 Go 团队,Colly 是务实的选择。已有 Scrapy 基础设施的 Python 团队应仔细评估迁移成本。
Colly 能抓取 JavaScript 渲染的网站吗? #
不能——Colly 原生不执行 JavaScript。对于 SPA 和动态内容,先使用 chromedp 或 Rod 渲染页面,然后将 HTML 传递给 Colly 解析。这种混合模式兼具 Chromium 的渲染能力和 Colly 的提取速度。
如何在多台机器上扩展 Colly? #
使用 Redis 支持的队列(colly/queue)在多个 worker 之间分发 URL。每个 worker 运行一个 Colly 实例,从共享队列消费并将结果写入中央数据库。在 Kubernetes 中添加 HPA 实现弹性容量。
哪些代理提供商与 Colly 配合最好? #
任何 HTTP 代理都可通过 colly/proxy 工作。Webshare 提供与 Colly RoundRobinProxySwitcher 干净集成的住宅代理和轮换 IP 池。Bright Data 和 Oxylabs 是具有专属支持的企业级替代方案。
如何避免抓取时被封锁? #
组合多种技术:通过 Colly 扩展轮换 User-Agent、添加随机延迟(LimitRule 中的 RandomDelay)、遵守 robots.txt、使用住宅代理、并随时间分布请求。永远不要超过目标站点的容量——监控响应码并在 429 错误时退避。
Colly 适合抓取数百万页面吗? #
是的,但需要适当的架构。使用 Redis 进行 URL 去重和缓存、使用时间戳实现增量爬取、跨多个 worker 分片、并持久化状态以在重启后恢复。团队报告每月使用 3-5 个 Colly 实例抓取 1,000 万+ 页面。
如何调试 Colly 抓取器? #
使用 colly.Debugger(&debug.LogDebugger{}) 启用调试日志以追踪每个请求/响应。使用 OnError 回调捕获和记录失败请求。对于复杂问题,附加带有请求/响应转储功能的自定义 HTTP 后端。
结论 #
Colly 为 Go 开发者提供了抓取框架所需的一切:速度、简洁和单二进制部署故事。以 25,302 个 GitHub Stars 和 每秒 1,000+ 请求的速度,它在吞吐量和内存效率方面优于 Python 和 Node.js 替代方案。回调 API 直观,Redis 集成支持真正的分布式爬取,代理支持让你在规模上保持畅通。
入门行动项:
- 克隆 Colly GitHub 仓库并运行
_examples/文件夹 - 使用上方 5 分钟配置构建你的第一个抓取器
- 在扩展到 1 万页之前添加 Redis 缓存和代理轮换
- 加入 dibi8 Telegram 群组参与 Go 抓取讨论和生产技巧分享
本文包含 Webshare 的附属链接。我们推荐已评估可用于生产抓取流程的服务。
推荐部署与基础设施 #
上述工具想要落地生产,靠谱的基础设施是前提。dibi8 自己也在用的两个选择:
- DigitalOcean — 新用户 60 天 $200 免费额度,14+ 全球节点。运行开源 AI 工具的首选。
- HTStack — 香港 VPS,国内访问低延迟,dibi8.com 自己也跑在它上面,生产环境验证过。
Aff 链接 — 不增加你的成本,但能帮 dibi8 持续运营。
来源与延伸阅读 #
- Colly GitHub 仓库 — 官方源代码和示例
- Colly 文档 — API 参考和教程
- Colly v2.2.0 发布说明 — 最新稳定版
- Scrapy 官方文档 — Python 抓取框架对比
- Puppeteer GitHub — Headless Chrome Node.js API
- goquery GitHub — Go 的 jQuery 风格 HTML 解析
- Web Scraping with Go: Practical Guide — 生产模式
- Best Open-Source Web Crawlers 2026 — 生态概览
- Colly Benchmarks — 性能数据
💬 留言讨论