You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Go Colly深度1并行爬取多链接?解决并行失效问题

解决Colly爬虫并行爬取且单站MaxDepth=1的问题

给你两种可行的解决思路:

方案一:单Collector实例+自定义深度控制

复用单个Collector实现全局并行,同时通过请求上下文(Context)为每个城市的爬取任务单独设置深度限制,替代全局MaxDepth的约束。

代码示例

import (
    "log"
    "github.com/gocolly/colly/v2"
    "github.com/gocolly/colly/v2/extensions"
)

func main() {
    dbutil.Init()
    defer dbutil.Close()
    db := dbutil.GetDB()

    // 初始化单个异步Collector,不设置全局MaxDepth
    c := colly.NewCollector(colly.Async(true))
    extensions.RandomUserAgent(c)
    // 设置全局并行度为20,统一控制所有请求的并发数
    c.Limit(&colly.LimitRule{DomainGlob: "*", Parallelism: 20})

    // 自定义深度校验:每个请求携带自己的最大深度限制
    c.OnRequest(func(r *colly.Request) {
        maxDepth, ok := r.Ctx.Get("max_depth").(int)
        if !ok {
            maxDepth = 1
        }
        // Colly中初始请求深度为0,子请求深度递增
        if r.Depth > maxDepth {
            r.Abort()
            return
        }
    })

    // 绑定你的爬虫处理逻辑(需调整spider函数,不再传入单个Collector)
    spider(c, db)

    // 从数据库读取城市链接
    rows, err := db.Query("SELECT id, link FROM cities_table")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    var cities []struct{ ID int; Link string }
    for rows.Next() {
        var id int
        var link string
        if err := rows.Scan(&id, &link); err != nil {
            log.Println(err)
            continue
        }
        cities = append(cities, struct{ ID int; Link string }{id, link})
    }

    // 遍历城市发起请求,每个请求携带专属的深度限制和城市ID
    for _, city := range cities {
        baseURL := ThroughProxy(city)
        ctx := colly.NewContext()
        ctx.Put("max_depth", 1)
        ctx.Put("city_id", city.ID)
        if err := c.Request("GET", baseURL.String(), nil, ctx, nil); err != nil {
            log.Println(err)
        }
    }

    c.Wait()
}

说明

  • 所有请求复用同一个Collector,全局并发度由Parallelism:20统一控制,避免资源浪费
  • 通过请求上下文为每个城市任务单独设置深度限制,保证单站的爬取深度不超过1
  • 需调整spider函数,通过r.Ctx.Get("city_id")获取当前请求对应的城市信息进行后续处理

方案二:Goroutine并行运行多个Collector

如果不想大幅修改现有代码,可以将每个城市的Collector实例化和爬取逻辑放到goroutine中,用WaitGroup等待所有任务完成,实现多Collector并行。

代码示例

import (
    "log"
    "sync"
    "github.com/gocolly/colly/v2"
    "github.com/gocolly/colly/v2/extensions"
)

func main() {
    dbutil.Init()
    defer dbutil.Close()
    db := dbutil.GetDB()

    rows, err := db.Query("SELECT id, link FROM cities_table")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    var cities []struct{ ID int; Link string }
    for rows.Next() {
        var id int
        var link string
        if err := rows.Scan(&id, &link); err != nil {
            log.Println(err)
            continue
        }
        cities = append(cities, struct{ ID int; Link string }{id, link})
    }

    var wg sync.WaitGroup
    wg.Add(len(cities))

    for _, city := range cities {
        // 启动goroutine处理单个城市的爬取
        go func(city struct{ ID int; Link string }) {
            defer wg.Done()
            c := colly.NewCollector(
                colly.MaxDepth(1),
                colly.Async(true),
            )
            extensions.RandomUserAgent(c)
            // 单个Collector的并行度建议调低,避免全局并发过高(比如设为2,10个goroutine总并发就是20)
            c.Limit(&colly.LimitRule{DomainGlob: "*", Parallelism: 2})
            spider(c, db, city)
            baseURL := ThroughProxy(city)
            c.Visit(baseURL.String())
            c.Wait()
        }(city) // 传值避免循环变量引用问题
    }

    wg.Wait()
}

说明

  • 每个城市对应一个独立Collector,保留原有的MaxDepth:1设置,无需修改深度逻辑
  • 通过goroutine实现多Collector并行,WaitGroup确保所有任务完成后再退出
  • 注意控制单个Collector的并行度,避免全局并发数过高触发反爬或耗尽本地资源

内容的提问来源于stack exchange,提问作者Farshad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 14:34:54