用 time.ticker 控制请求节奏最轻量可靠:它不依赖系统 cron、不引入外部服务、进程重启不丢任务,配合 channel 即可精准控频,避免常见误用。

用 time.Ticker 控制请求节奏最轻量可靠
Go 做定时爬虫,别一上来就堆 cron 或第三方调度库——多数场景下,time.Ticker 配合 channel 就够用,且完全可控。它不依赖系统 cron,不引入外部服务,也不会因进程重启而丢任务。
常见错误是直接 后立刻起 goroutine,却不等结果完成,导致并发失控或 panic:
// ❌ 错误示范:没等 resp.Body.Close,也没控并发
for _, url := range urls {
<p>正确做法是把 ticker 当“节拍器”,每次只放行一个请求,并用 <code>sync.WaitGroup</code> 确保清理:</p>
- 用
ticker := time.NewTicker(5 * time.Second)设固定间隔(比如每 5 秒抓一次) - 循环中先
,再同步执行 <code>fetch(url)(非 goroutine) - 若需并发采集多个目标,把 ticker 放在 worker goroutine 内部,而非外层循环
- 务必在
fetch函数里显式defer resp.Body.Close(),goroutine 中的 defer 是有效的
如何避免被封?关键在 Client 设置和跳转控制
很多爬虫跑几小时就 429 或连接重置,不是因为频率高,而是 header 缺失、跳转失控、cookie 混乱。标准 http.Client 默认行为对爬虫很不友好。
必须手动构造 client 并覆盖默认行为:
-
Client.CheckRedirect设为func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },禁用自动跳转,自己用resp.Header.Get("Location")处理,否则 cookie 和 referer 会丢失 -
Client.Timeout显式设为 10–30 秒,防止某个页面卡死拖垮整个定时任务 -
Transport.TLSClientConfig.InsecureSkipVerify = true仅限调试 HTTPS 自签名站,上线前必须删掉,否则有安全风险 - 一定要加
User-Agentheader,比如"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",否则部分站点直接返回 403
解析 HTML 别碰正则,goquery 的容错写法才是日常
用 regexp 提取标题、链接、正文?只要网页多一个空格、换行、属性顺序调换,就全崩。这不是理论风险,是每天都会发生的线上故障。
github.com/PuerkitoBio/goquery 是目前 Go 生态最稳的选择,但用法不对照样翻车:
- 永远用
s.Text()取文本,不用s.Nodes[0].FirstChild.Data—— 前者自动合并文本节点、去首尾空白;后者在注释、CDATA 或嵌套标签下直接 panic - 用
s.Attr("href")获取属性,它返回(string, bool),比s.Get(0).Attr安全得多(后者属性不存在时 panic) - 别写
doc.Find("div").Eq(1),优先用语义 selector,比如"article h1", "main .content p:first-of-type",结构微调也不易断 - 相对 URL 一定要用
url.Join(baseURL, href)拼接,别字符串拼接,否则//example.com或/path会出错
定时任务不能只靠 ticker,得加失败重试 + 持久化状态
真实场景中,网络抖动、目标站临时 502、DNS 解析失败太常见。如果每次失败就跳过,几天后数据就断层了。
简单但有效的补救方式:
- 单次请求封装成带重试的函数:
fetchWithRetry(url, maxRetries: 3, backoff: 2*time.Second),指数退避比固定等待更抗抖动 - 记录最后成功抓取的时间戳到本地文件或 Redis,下次启动时从该时间点继续,避免重复或遗漏
- 把 ticker 放进 for-select 循环里,监听
os.Signal实现优雅退出,避免 kill -9 导致中间状态丢失 - 日志必须带上下文:用
log.Printf("[fetch] %s → %d (%v)", url, resp.StatusCode, err),别只打 “failed”
定时爬虫最难的从来不是“怎么触发”,而是“怎么扛住各种失败还保持数据连续”。ticker 只是节拍器,真正要花力气的是围绕它的容错链路。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











