当前位置:首页 > 文章列表 > Golang > Go教程 > Go Colly 怎么限制公开页面采集的并发和间隔

Go Colly 怎么限制公开页面采集的并发和间隔

来源:17golang原创 2026-10-13 15:44:53 0浏览 收藏

我第一次把 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。

Go Colly LimitRule 中域名匹配、并发上限和请求间隔的关系结构图
图1:Colly LimitRule 的域名匹配、并发上限与请求间隔关系说明图,不是运行截图或采集证据。

先把异步开关和限流参数分开

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
}
Go Colly 为不同公开站点分别配置限流规则的结构关系图
图2:多个公开站点分别绑定 Colly 限流规则的静态边界说明图,不是浏览器或终端截图。

这两条规则的重点不是参数大小,而是作用域隔离: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、网络响应、回调处理和站点响应时间影响。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go goquery Find 与 Filter 选择节点有什么区别Go goquery Find 与 Filter 选择节点有什么区别
上一篇
Go goquery Find 与 Filter 选择节点有什么区别
Go Colly 怎么在回调之间传递请求级业务信息
下一篇
Go Colly 怎么在回调之间传递请求级业务信息
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    460次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    521次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    539次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    487次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    307次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码