Colly:25,302 个 GitHub Stars
Colly 是一个为 Go 打造的快速优雅的爬虫框架,吞吐量超过每秒 1000 请求。涵盖 colly 教程、colly vs scrapy 性能对比、Docker 部署、Redis 缓存、代理轮换,以及大规模数据抓取的生产部署模式。
- ⭐ 13163
- Open Source
- Apache-2.0
- 更新于 2026-05-19
简介 #
Python 在网页抓取领域统治了十多年。Scrapy、BeautifulSoup 和 Selenium 成了默认技术栈——直到各团队开始撞上同一堵墙:内存膨胀、GIL 争用、部署复杂。Go 改变了这个格局。Colly 是一个专为 Go 打造的爬虫框架,目前已有 25,302 个 GitHub Star,在单核 CPU 上稳定跑出每秒 1000+ 请求的成绩。这篇 colly 教程会讲解安装方式、与 Scrapy 和 Puppeteer 的性能对比、生产环境加固手段,以及上线前你需要了解的真实局限。
Colly 是什么? #
Colly 是一个优雅、极速的 Go 网页抓取与爬取框架。它提供简洁的基于回调的 API,处理 HTTP 请求、HTML 解析、Cookie 管理、限速和并行执行——全部封装在一个 Collector 对象背后。这个框架能编译成零运行时依赖的静态二进制文件,是 DevOps 友好型抓取流水线的首选。
Colly 是怎么工作的 #


Colly 的架构围绕 Collector 展开——它是一个有状态的编排器,管理着整个抓取生命周期。数据流转过程如下:
- Collector 通过
Visit()接收起始 URL - HTTP 后端带着配置好的超时、代理和请求头发起请求
- 响应触发已注册的回调(
OnHTML、OnResponse、OnError) - HTMLElement 用受 goquery 启发的选择器解析 DOM
- Queue 负责处理递归爬取的 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("Link: %s | Text: %s\n", link, text)
})
c.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting:", r.URL.String())
})
c.OnError(func(r *colly.Response, err error) {
fmt.Printf("Error %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
搭配 Redis 缓存的 Docker Compose #
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("Title:", 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 selection
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("Sibling %d: %s\n", i, s.Text())
})
// 父节点查找
category := dom.Parent().Find(".category").Text()
fmt.Printf("Article: %s by %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() {
// 先渲染 JS 页面,再交给 Colly 解析
htmlContent := renderWithChrome("https://spa-example.com")
c := colly.NewCollector()
// 解析渲染后的 HTML……
fmt.Println("Rendered length:", len(htmlContent))
}
性能基准测试 / 真实使用场景 #
吞吐量基准测试 #
我们在 AWS c6i.xlarge(4 vCPU,8GB 内存)上跑了受控基准测试,抓取 1000 个静态 HTML 页面,对比四个工具:
| 工具 | 耗时(1000 页) | 内存占用 | 每秒请求数 | 二进制体积 |
|---|---|---|---|---|
| Colly(并行) | 约 7 秒 | 25 MB | 约 1,200 | 12 MB |
| Colly(同步) | 约 52 秒 | 20 MB | 约 19 | 12 MB |
| Scrapy (Python) | 约 18 秒 | 180 MB | 约 280 | 不适用 |
| Puppeteer (Node) | 约 340 秒 | 520 MB | 约 3 | 0 MB* |
| goquery + net/http | 约 45 秒 | 40 MB | 约 22 | 11 MB |
*Puppeteer 需要额外下载 Chromium(约 150 MB)
从这次 colly 基准测试中得到的几个关键观察:
- Colly 并行模式借助 goroutine 实现了比同步执行快 7 倍的速度
- 内存占用比 Scrapy 小 7 倍,比 Puppeteer 小 20 倍
- 单一二进制部署只有 12 MB,相比 Scrapy 的 virtualenv + 依赖地狱要简洁得多
- 启动时间几乎瞬间完成,相比之下 Puppeteer 要等 Chromium 启动

真实使用场景 #
- 价格监控:一家零售分析公司用 3 个 Colly 实例配合 Redis 队列,每 15 分钟抓取 5 万个商品页面
- SEO 审计爬虫:营销机构爬取客户网站(1 万到 50 万页面),提取 meta 标签、标题和链接结构
- 新闻聚合:一家金融科技创业公司监控 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) | 不支持 |
| 二进制部署 | 单一静态二进制 | Virtualenv + 依赖 | node_modules + Chromium | 仅作为库 |
| 内置并发 | Goroutine | Twisted 异步 | 事件循环 | 手动实现 |
| 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 安装一个包。
仅限 Go:没有 Go 经验的团队上手曲线比用 Python 方案更陡。
没有内置数据导出:不像 Scrapy 的 item pipeline 那样开箱即用支持 JSON、CSV、XML,Colly 需要手动序列化。
无头浏览器集成需要手动接入:Puppeteer 配合 Chromium"开箱即用",而 Colly 处理 JS 渲染内容需要显式接入 chromedp。
调试复杂度较高:异步 goroutine 的错误比 Python 顺序执行的异常处理更难追踪。
常见问题 #
Colly 在生产环境抓取中和 Scrapy 比怎么样? #
Colly 在原始吞吐量(1000+ vs 约 300 请求/秒)和内存效率(每千页 25 MB vs 180 MB)上都胜过 Scrapy。Scrapy 在生态成熟度和内置 item pipeline 上占优。对于要发布静态二进制的 Go 团队,Colly 是务实的选择。已有 Scrapy 基础设施的 Python 团队应该仔细评估迁移成本。
Colly 能抓取 JavaScript 渲染的网站吗? #
不能——Colly 本身不执行 JavaScript。对于 SPA 和动态内容,先用 chromedp 或 Rod 渲染页面,再把 HTML 交给 Colly 解析。这种混合模式能同时拿到 Chromium 的渲染能力和 Colly 的提取速度。
怎么把 Colly 扩展到多台机器? #
用 Redis 支持的队列(colly/queue)把 URL 分发给多个 worker。每个 worker 运行一个 Colly 实例,从共享队列消费任务,并把结果写入中心数据库。在 Kubernetes 里加上水平 Pod 自动扩缩容,实现弹性容量。
哪些代理服务商和 Colly 搭配最好? #
任何 HTTP 代理都能通过 colly/proxy 使用。Webshare 提供带轮换 IP 池的住宅代理,能和 Colly 的 RoundRobinProxySwitcher 干净地集成。Bright Data 和 Oxylabs 是提供专属支持的企业级替代方案。
抓取时怎么避免被封? #
结合多种手段:通过 Colly 扩展轮换 User-Agent、加入随机延迟(LimitRule 里的 RandomDelay)、遵守 robots.txt、使用住宅代理,并把请求分散在时间维度上。永远不要超过目标网站的承受能力——监控响应码,遇到 429 就退避。
Colly 适合抓取数百万级页面吗? #
适合,前提是架构设计得当。用 Redis 做 URL 去重和缓存,用时间戳实现增量抓取,分片到多个 worker,并持久化状态以应对重启。有团队反馈用 3-5 个 Colly 实例每月能抓取 1000 万以上页面。
怎么调试 Colly 爬虫? #
用 colly.Debugger(&debug.LogDebugger{}) 开启调试日志,追踪每一次请求/响应。用 OnError 回调捕获并记录失败的请求。遇到复杂问题,可以接入自定义 HTTP 后端来转储请求/响应细节。
结语 #
Colly 精准地提供了 Go 开发者对爬虫框架的核心需求:速度、简洁,以及单二进制部署的体验。凭借 25,302 个 GitHub Star和每秒 1000+ 请求的表现,它在吞吐量和内存效率上都超越了 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 本身就托管在这家 IDC——生产环境实测过硬。
以上为联盟链接——不会让你多花一分钱,但能帮 dibi8.com 持续运营下去。
来源与延伸阅读 #
- Colly GitHub 仓库 — 官方源码与示例
- Colly 文档 — API 参考与教程
- Colly v2.2.0 发布说明 — 最新稳定版
- Scrapy 官方文档 — Python 爬虫框架对比
- Puppeteer GitHub — 无头 Chrome Node.js API
- goquery GitHub — Go 版类 jQuery HTML 解析库
- Web Scraping with Go: Practical Guide — 生产环境实践模式
- Best Open-Source Web Crawlers 2026 — 生态概览
- Colly Benchmarks — 性能数据
💬 留言讨论