Go Colly 怎么限制公开页面采集的并发和间隔
我第一次把 Colly 切到异步采集时,最先看到的不是速度提升,而是请求一下子挤到目标站点:代码里虽然写了 Async(),却没有给每个域名设置节奏。解决这类问题的关键不是在回调里手写一个全局 time.Sleep,而是给 Collector 配置 LimitRule,让域名、并发和间隔成为一组明确的策略。
这篇只讨论允许访问的公开页面采集。最实用的判断是:想限制“同时在飞”的请求,用 Parallelism;想让同一域名的请求按时间拉开,用 Delay,必要时再加 RandomDelay。如果启用了异步采集,最后还要调用 Wait(),否则主函数可能在回调完成前退出。
官方地址:https://github.com/gocolly/colly
LimitRule 到底限制了什么
Colly 的 LimitRule 先用 DomainGlob 或 DomainRegexp 找到要约束的域名,再决定如何限制连接。官方 API 文档将它拆成两个核心方向:Parallelism 是匹配域名允许同时存在的最大请求数;Delay 是创建下一次请求前要等待的时间,RandomDelay 则是在固定间隔上再增加一个随机等待。
这里有一个很容易踩到的语义:当规则使用 Delay 时,官方说明将该规则视为串行节奏,也就是并发按 1 处理。不要把 Delay: 2 * time.Second 和 Parallelism: 4 理解成“4 个并发、每个之间都延迟 2 秒”;如果目标是并发上限,就先只配 Parallelism,如果目标是同域请求之间的时间间隔,就使用 Delay。

先把异步开关和限流参数分开
Async 解决的是 Collector 是否并行发起网络工作,不是目标域名的流量配额。开启它后,HTML 回调中继续调用 Visit 也不会自动变成“无限制采集”;真正的同域请求上限要交给 LimitRule。反过来,只配了 Parallelism,也不代表整个程序会自动等待所有异步请求结束。
下面的示例给一个公开站点设置最多两个并发请求,并在主流程结束前等待所有采集任务。示例里的域名只是演示匹配范围,换成你明确获准访问的站点,并遵守其 robots.txt、服务条款和访问频率要求。
package main
import (
"log"
"time"
"github.com/gocolly/colly/v2"
)
func main() {
// Async 只打开并行网络工作,不能代替每个域名的限流规则。
c := colly.NewCollector(
colly.Async(),
colly.AllowedDomains("example.com"),
)
// DomainGlob 限定规则作用域,Parallelism 限制同域同时在飞的请求数。
if err := c.Limit(&colly.LimitRule{
DomainGlob: "example.com",
Parallelism: 2,
}); err != nil {
// 规则初始化失败时立即退出,避免用未预期的并发继续采集。
log.Fatal(err)
}
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
// 回调内只提交符合范围的链接,实际请求仍由 Collector 调度。
if err := e.Request.Visit(e.Attr("href")); err != nil {
log.Printf("visit failed: %v", err)
}
})
if err := c.Visit("https://example.com/"); err != nil {
log.Fatal(err)
}
// Async 模式必须等待,否则进程可能在回调和请求完成前退出。
c.Wait()
}
这段代码适合“可以有少量并发,但不希望同域请求无限增长”的场景。Parallelism: 2 是单个匹配域名的上限,不是所有目标域名的全局上限;如果采集器还会访问别的域名,需要给它们分别配置规则,或在业务层进一步控制总任务量。
需要固定间隔时怎么写
如果目标站点更关心请求之间的节奏,而不是尽可能多地同时处理请求,就使用 Delay。它表达的是“向匹配域名创建下一次请求前至少等待多久”。为了避免请求呈现完全固定的节拍,可以加上 RandomDelay,让每次等待在固定间隔之上增加随机时长。
package main
import (
"log"
"time"
"github.com/gocolly/colly/v2"
)
func buildCollector() (*colly.Collector, error) {
// 这里不打开 Async,让示例把“请求间隔”作为主要节奏;需要异步时可按任务模型开启。
c := colly.NewCollector(colly.AllowedDomains("example.com"))
// Delay 是固定等待,RandomDelay 是额外随机等待;该规则按串行节奏处理。
if err := c.Limit(&colly.LimitRule{
DomainGlob: "example.com",
Delay: 1500 * time.Millisecond,
RandomDelay: 500 * time.Millisecond,
}); err != nil {
return nil, err
}
c.OnError(func(r *colly.Response, err error) {
// 记录 URL 和错误,便于区分站点响应问题与本地解析问题。
log.Printf("url=%s status=%d err=%v", r.Request.URL, r.StatusCode, err)
})
return c, nil
}
固定间隔不是“保证每次响应都相隔固定秒数”。网络响应时间、回调处理和站点本身排队都会影响端到端耗时;它主要约束的是 Colly 创建下一次匹配请求的节奏。RandomDelay 也不是精准的速率控制器,而是给固定间隔增加一个随机上界,适合不希望请求呈现机械节拍的工程场景。
多站点采集怎么拆规则
我觉得最稳妥的做法是“一个站点一条规则”:把允许的域名、并发上限、固定间隔和随机间隔放在同一条规则里,避免用一套参数覆盖完全不同的服务。DomainGlob 适合简单的域名匹配;需要更细的边界时使用 DomainRegexp,并提前确认它实际能匹配到目标主机名。
package main
import (
"log"
"time"
"github.com/gocolly/colly/v2"
)
func newMultiSiteCollector() *colly.Collector {
// 让 Collector 负责异步调度,但把每个站点的节奏拆开管理。
c := colly.NewCollector(colly.Async())
// 新闻站点只允许少量并发,避免一个站点占满采集器。
if err := c.Limit(&colly.LimitRule{
DomainGlob: "news.example.com",
Parallelism: 2,
}); err != nil {
log.Fatal(err)
}
// 文档站点采用固定间隔,并额外增加一个小的随机等待。
if err := c.Limit(&colly.LimitRule{
DomainGlob: "docs.example.org",
Delay: 2 * time.Second,
RandomDelay: 800 * time.Millisecond,
}); err != nil {
log.Fatal(err)
}
return c
}

这两条规则的重点不是参数大小,而是作用域隔离:news.example.com 的两个并发不会自动变成 docs.example.org 的两个并发。对于同一组织下的多个子域名,要先决定限制对象是每个主机名,还是需要用更宽的匹配方式把它们纳入同一策略;不要只看 URL 路径就假设它们共享一个额度。
为什么设置了限制却感觉没有生效
- 规则没有匹配域名:先检查
DomainGlob或正则表达式对应的是主机名,而不是带协议的完整 URL。 - 把 Async 当成限流器:
Async只影响调度方式,必须额外设置LimitRule。 - 把 Delay 和 Parallelism 当成叠加:使用
Delay的规则按串行间隔理解,不要期待它同时保持高并发。 - 忘记 Wait:异步任务还没有结束,程序就退出时,看起来像是只处理了一部分链接。
- 只限制了入口域名:页面中的绝对链接可能指向别的主机名;要么收窄
AllowedDomains,要么为需要访问的站点分别配置规则。 - 把回调耗时算成请求间隔:解析、写文件或入库在回调中执行时,会改变整体吞吐,不能只用它推断 LimitRule 的效果。
我会怎样收口参数
我通常先从较保守的配置开始:单站点任务用 Parallelism: 1 或一个明确的 Delay,确认错误率、响应时间和目标站点的访问约束后,再逐步提高并发。公开页面采集不应该以“越快越好”为唯一目标,稳定完成、可恢复和不影响对方服务更重要。
| 目标 | 优先参数 | 需要留意 |
|---|---|---|
| 限制同域同时请求数 | Parallelism | 只限制匹配域名,不是整个进程的总任务数 |
| 拉开同域请求时间 | Delay | 规则按串行节奏处理,不能理解为高并发加睡眠 |
| 避免固定节拍 | Delay + RandomDelay | 随机等待是额外范围,不是精确速率计 |
| 保证异步采集收尾 | c.Wait() | 放在入口任务提交之后,避免进程提前结束 |
归纳起来,Go Colly 限制公开页面采集并发和间隔的入口就是 LimitRule:先用域名把边界划清,再在并发上限和时间间隔之间选清楚语义。我的经验是,宁可先采用较小并发和可观察的间隔,再依据真实任务结果调整,也不要把全局 Sleep 塞进回调里,让不同站点互相拖慢。
相关问题
Colly 的 Async 会不会自动限制并发?
不会。Async 负责异步网络调度,匹配域名的最大并发需要通过 LimitRule 的 Parallelism 设置。
Delay 和 RandomDelay 应该一起使用吗?
不一定。只需要固定间隔时使用 Delay;不希望每次都呈现完全固定的节拍时,再在它上面增加 RandomDelay。
能不能用一个 LimitRule 限制所有站点?
可以使用更宽的匹配方式,但通常不建议直接套用同一参数。不同站点的承载能力和访问约束可能不同,拆成多条规则更容易解释和调整。
为什么调用了 Wait 还是很慢?
Wait 只负责等待异步任务结束,不会提高吞吐。实际速度还会受到 Delay、RandomDelay、Parallelism、网络响应、回调处理和站点响应时间影响。
Go goquery Find 与 Filter 选择节点有什么区别
- 上一篇
- Go goquery Find 与 Filter 选择节点有什么区别
- 下一篇
- Go Colly 怎么在回调之间传递请求级业务信息
-
- Golang · Go教程 | 29分钟前 |
- Go Goldmark 怎么遍历 AST 提取标题目录
- 174浏览 收藏
-
- Golang · Go教程 | 32分钟前 |
- Go Goldmark 怎么给 Markdown 渲染开启表格扩展
- 119浏览 收藏
-
- Golang · Go教程 | 49分钟前 |
- Go GJSON 怎么一次读取多个不同路径的值
- 311浏览 收藏
-
- Golang · Go教程 | 49分钟前 |
- Go GJSON 怎么读取键名包含点号的 JSON 字段
- 111浏览 收藏
-
- Golang · Go教程 | 51分钟前 |
- Go GJSON 怎么只提取 JSON 数组中的指定字段
- 312浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go Colly 怎么给列表页和详情页使用独立回调
- 298浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go Colly 怎么在回调之间传递请求级业务信息
- 389浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go goquery 怎么移除副本中的节点而保留原文档
- 305浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go goquery 怎么区分属性缺失和属性值为空
- 272浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go goquery 怎么把一组 HTML 节点映射成结构化数据
- 173浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 460次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 521次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 539次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 487次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 307次使用
-
- Go error wrapping 实战:别让错误日志只剩一句 failed
- 2026-06-01 151浏览
-
- Go pprof 排查慢接口:别只会看火焰图,先把问题问对
- 2026-06-01 101浏览
-
- Go Flight Recorder 实战:线上偶发卡顿,别再只靠日志碰运气
- 2026-06-01 323浏览
-
- Go testing/synctest 实战:别再用 time.Sleep 赌并发测试会过
- 2026-06-01 428浏览
-
- Go slog 生产实践:日志别只会打印 error,要能帮你排障
- 2026-06-01 143浏览

