GO CRAWLING FRAMEWORK

Golang Colly代理爬虫

Colly使用官方proxy switcher接入123Proxy网关,CSS回调解析Quotes to Scrape,自动访问下一页,并将结构化字段写入CSV。

语言Go 1.22+框架Colly v2测试目标Quotes to Scrape输出quotes-colly.csv
打开对应产品提取 查看测试目标
01
END-TO-END RESULT

分页采集、限速与CSV输出

测试目标Quotes to Scrapehttps://quotes.toscrape.com/ 运行时Go 1.22+Colly v2 输出文件结构化数据quotes-colly.csv

代码直接连接隧道代理固定网关 proxy.123proxy.cn:36923。主机与端口无需替换,出口路由由当前产品配置决定;你只需设置控制台生成的完整代理用户名和密码。

02
CREDENTIALS

只把代理账密放进环境变量

PROXY_USER必须是控制台中的代理用户名,不是网站登录手机号。地区和SESSION场景应复制控制台生成的完整路由用户名。

export PROXY_USER="替换为控制台代理用户名"
export PROXY_PASS="替换为控制台代理密码"
# 隧道代理固定网关:proxy.123proxy.cn:36923

不要把真实代理密码提交到Git仓库、镜像、日志或前端代码中。

03
COMPLETE PROGRAM

main.go

以下代码包含完整入口和输出逻辑。安装注释、运行命令与依赖声明保留在文件顶部,复制为对应文件后即可执行。

// initialize:
//   go mod init example.com/quotes-proxy
//   go get github.com/gocolly/colly/v2
// run:
//   go run .
package main

import (
    "encoding/csv"
    "fmt"
    "log"
    "net/url"
    "os"
    "strings"
    "time"

    "github.com/gocolly/colly/v2"
    collyproxy "github.com/gocolly/colly/v2/proxy"
)

func main() {
    host := "proxy.123proxy.cn"
    port := 36923
    username := os.Getenv("PROXY_USER")
    password := os.Getenv("PROXY_PASS")
    if username == "" || password == "" {
        log.Fatal("set PROXY_USER and PROXY_PASS first")
    }

    proxyURL := &url.URL{
        Scheme: "http",
        Host:   fmt.Sprintf("%s:%d", host, port),
        User:   url.UserPassword(username, password),
    }
    switcher, err := collyproxy.RoundRobinProxySwitcher(
        proxyURL.String(),
    )
    if err != nil {
        log.Fatal(err)
    }

    output, err := os.Create("quotes-colly.csv")
    if err != nil {
        log.Fatal(err)
    }
    defer output.Close()
    writer := csv.NewWriter(output)
    defer writer.Flush()
    _ = writer.Write([]string{"text", "author", "tags", "source_url"})

    collector := colly.NewCollector(
        colly.AllowedDomains("quotes.toscrape.com"),
        colly.UserAgent("123Proxy-Developer-Example/1.0"),
    )
    collector.SetProxyFunc(switcher)
    _ = collector.Limit(&colly.LimitRule{
        DomainGlob:  "*toscrape.com",
        Parallelism: 2,
        RandomDelay: 500 * time.Millisecond,
    })

    collector.OnHTML("div.quote", func(element *colly.HTMLElement) {
        tags := element.ChildTexts("a.tag")
        _ = writer.Write([]string{
            strings.TrimSpace(element.ChildText("span.text")),
            strings.TrimSpace(element.ChildText("small.author")),
            strings.Join(tags, "|"),
            element.Request.URL.String(),
        })
    })
    collector.OnHTML("li.next > a", func(element *colly.HTMLElement) {
        _ = element.Request.Visit(element.Attr("href"))
    })
    collector.OnError(func(response *colly.Response, requestErr error) {
        log.Printf(
            "request failed: url=%s status=%d error=%v",
            response.Request.URL,
            response.StatusCode,
            requestErr,
        )
    })

    if err := collector.Visit("https://quotes.toscrape.com/"); err != nil {
        log.Fatal(err)
    }
    fmt.Println("saved records to quotes-colly.csv")
}
04
ENGINEERING CHECKS

案例没有省略的关键逻辑

01net/url安全构造带账密的代理URL
02SetProxyFunc绑定到Collector
03AllowedDomains限制爬虫边界
04LimitRule控制并发和随机延迟
05
SWITCH PROXY PRODUCT

替换网关,不改采集逻辑

代理产品网关域名端口适合任务
隧道代理proxy.123proxy.cn36923混合池 / 纯住宅池
隧道住宅代理residential.123proxy.cn33000国家地区与SESSION
不限量动态住宅unlimit.residential.123proxy.cn10253不限流量住宅任务
两类静态代理控制台分配的固定代理IP返回端口直连IP,无网关域名
隧道住宅代理的国家与SESSION写在完整代理用户名中

网关固定为 residential.123proxy.cn:33000。例如使用美国SESSION时,直接把控制台生成的完整用户名作为 PROXY_USER,代码无需解析或重组。

06
PRIMARY REFERENCES

框架文档与测试目标

07
TROUBLESHOOTING

常见问题

只有一个网关为什么使用RoundRobinProxySwitcher?

它是Colly官方提供的代理接入函数,单个123Proxy网关同样可用;出口轮转由代理网络完成。

如何接入不限量动态住宅?

把代理地址固定为unlimit.residential.123proxy.cn:10253,并使用控制台生成的代理账密;无需在程序中猜测或生成网关端口。

如何处理429?

在OnError中记录状态码,按目标域名降低Parallelism并增加RandomDelay。不要无上限立即重试。