Go的http.Client必须显式配置超时,因默认无超时易致请求无限挂起;应禁用http.Get,改用自定义Client并设置Timeout、Transport各阶段超时及连接池参数。

http.Client 必须自定义,否则超时会卡死
Go 的 http.Get 用的是全局默认 client,它没有设置任何超时——遇到 DNS 解析卡住、服务端不响应或 TLS 握手失败时,Get 会无限挂起,不是 bug,是设计如此。你得自己管。
- 永远别用
http.Get做生产爬虫;改用&http.Client{Timeout: 10 * time.Second} - 只设
Timeout不够,还要配Transport:建议至少加IdleConnTimeout(复用连接空闲上限)和TLSHandshakeTimeout(避免 TLS 卡死) -
MaxIdleConns和MaxIdleConnsPerHost必须设,否则高并发下很快触发 “too many open files” 错误
goquery 找不到元素?先确认 HTML 里真有
goquery 不执行 JS,也不等页面渲染,它只解析你拿到的原始响应体。很多“找不到”本质是目标内容压根不在返回的 HTML 里。
- 先用
io.Copy(os.Stdout, resp.Body)或写入文件,人工打开看源码里有没有你要的div.item或class="product-title" - 如果没找到,说明是前端 JS 渲染生成的,goquery 无解,得换
rod或playwright-go - 注意编码:中文站常见
charset=gb2312,但 Go 默认按 UTF-8 解析,乱码会导致 selector 失效;要用golang.org/x/text/transform转码 - CSS 选择器区分大小写和空格:
Find("div.item")匹配不了class="Item",也匹配不了<div class="item"><span>text</span></div>里的span—— 得用Children()或Find("div.item span")
并发控制不是开 goroutine 数量,而是控连接与请求节奏
goroutine 是轻量,但 HTTP 客户端底层是 TCP 连接 + DNS 查询 + 系统文件描述符,三者都有限制。无脑 go fetch(url) 1000 次,不出三秒就崩。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 用带缓冲的 channel 控制并发数,比如
sem := make(chan struct{}, 5),每次请求前sem ,结束后 <code> -
http.Transport的MaxConnsPerHost建议设为 20–50,太高容易被目标站限流或封 IP - 加随机延时比固定延时更安全:
time.Sleep(time.Millisecond * time.Duration(rand.Intn(300) + 200)),避免被识别为扫描器 - 别省略
resp.Body.Close(),漏掉会导致连接无法复用,MaxIdleConns形同虚设
proxypool 编译集成时最容易漏掉验证环节
proxypool 编译出来是个命令行工具,不是库,它本身不提供 API;想集成进爬虫,得让它跑起来并暴露 HTTP 接口,然后你的爬虫从该接口取代理,再喂给 http.Transport。
立即学习“go语言免费学习笔记(深入)”;
- 编译后运行
./proxypool -c config.yaml,确认它监听了:8081(默认)且能返回有效代理列表 - 你的爬虫不能直接调
proxypool的二进制,而是要发 HTTP 请求到http://localhost:8081/get拿代理,再构造http.ProxyURL - 拿到代理后必须验证可用性:用该代理发一个 HEAD 请求到
http://httpbin.org,超时或状态非 200 就丢弃,否则白搭 - 不要把代理池当万能盾牌——User-Agent 轮换、Referer 设置、Cookie 管理一样不能少,否则单靠换 IP 也撑不过两轮请求
实际部署时,proxypool 和爬虫进程最好分离;共用一个配置文件容易因路径或权限问题导致代理拿不到,调试成本远高于多启一个进程。

















