生产环境必须自定义*http.Client并设Timeout(至少10秒),手动关闭resp.Body,禁用自动重定向,用goquery解析HTML时需处理编码、空指针和选择器失效,并用信号量控制并发。

用 net/http 发请求,不设超时就等于卡死
Go 的 http.Get 看似简单,但默认客户端没有超时,一遇到网络抖动或目标服务器响应慢,goroutine 就永远挂起。生产环境必须自定义 *http.Client。
-
Timeout是最基础的防护:至少设10 * time.Second,别信“等一会儿就好” - 别直接用
http.DefaultClient—— 它不会自动关连接池里的空闲连接,容易耗尽文件描述符 - 手动关闭
resp.Body不是可选项:defer resp.Body.Close()必须紧跟在http.Do或http.Get之后,否则下一次请求可能失败并报too many open files - 重定向默认自动跟随,但很多反爬页面靠 302 + JS 跳转绕过检测;想自己控制,就设
CheckRedirect返回http.ErrUseLastResponse
解析 HTML 别手写正则,用 goquery 但得防空指针
正则匹配 HTML 是自我惩罚:大小写、换行、属性顺序、注释都会让表达式失效。标准库 golang.org/x/net/html 虽安全,但写个“取所有 a 标签的 href”要十几行递归代码。
- 装
goquery:go get github.com/PuerkitoBio/goquery,它底层还是用原生html包,只是加了 jQuery 风格选择器 - 加载时别先读成字符串再转
strings.NewReader—— 直接传resp.Body给goquery.NewDocumentFromReader,省内存也避免编码丢失 -
.Find("xxx").Each(...)前务必检查.Size() > 0,选错选择器不会 panic,只会静默跳过,你根本不知道数据丢了 - 中文网页常见 GBK/GB2312 编码,
goquery默认按 UTF-8 解析会乱码;要用golang.org/x/net/html/charset.NewReaderLabel先做自动识别和转换
并发不是开越多 goroutine 越快,而是靠 sem 和 sync.Map 控节奏
对每个 URL 起一个 goroutine,看着很 Go,实际分分钟被封 IP 或触发目标站限流。真正可控的并发,靠的是信号量 + 去重 + 队列。
- 用带缓冲的 channel 当信号量:
sem := make(chan struct{}, 5),每次请求前sem ,结束后 <code><-sem - URL 去重不能只靠本地 slice 查找 —— 并发下竞态明显;用
sync.Map存已访问 URL(m.LoadOrStore(url, true)),比 map + mutex 更轻量 - 新发现的链接别立刻
go crawl(url),推到一个chan string里,由固定数量 worker 消费,实现 BFS 式可控遍历 - 别用
time.Sleep模拟延时 —— 它阻塞整个 goroutine;真要控频,用rate.Limiter(golang.org/x/time/rate)更稳妥
绕过 robots.txt 和伪造 User-Agent 不是“技术高”,是埋雷
很多新手觉得“我技术好,能绕过去”,结果跑两天就被 429 或 TCP RST。合规不是道德选择,是稳定运行的前提。
立即学习“go语言免费学习笔记(深入)”;
- 发起任何请求前,先 GET
/robots.txt,用golang.org/x/net/robotstxt解析,调txt.TestAgent(path, "my-crawler")检查是否允许抓取 -
User-Agent必须设真实、可追溯的值,比如"my-crawler/1.0 (+https://github.com/you/repo)";填浏览器 UA 是自找麻烦,多数 WAF 一看就拦截 - 日志里别打完整 URL,尤其带参数的 —— 可能泄露 token、session_id;记录前先用
url.URL{Scheme: u.Scheme, Host: u.Host, Path: u.Path}截断 - 目标站若返回
403,第一反应不该是换 UA,而是检查是否漏了Accept、Accept-Language头,或者 robots.txt 已禁止该路径
真正的难点不在“怎么拿到数据”,而在“怎么拿得久、拿得稳”。网络不稳定、页面结构突变、编码自动识别失败、并发数调错导致连接打满……这些都不是报错就停的 bug,而是悄悄吃掉成功率的暗坑。留点余量,多做一层判断,比追求“一次性跑通”重要得多。


















