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 Scrape
https://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$env:PROXY_USER="替换为控制台代理用户名"
$env: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。不要无上限立即重试。