用net/http发GET请求需自定义Client设超时、手动Close响应体、谨慎处理重定向;解析HTML推荐goquery,注意编码与选择器判空;并发需用channel限流。

用 net/http 发起 GET 请求是最直接的起点
Go 自带的 net/http 足够发起基础请求,不需要额外依赖。关键不是“能不能发”,而是怎么处理响应体、超时和重定向。
-
http.DefaultClient默认不设超时,生产环境必须自定义http.Client并设置Timeout - 响应体(
resp.Body)必须手动Close(),否则会泄漏 HTTP 连接 - 如果目标站有重定向,
http.Get()默认跟随,但可能跳到非预期 URL;需要控制时,应禁用CheckRedirect并自己处理 - 示例中常见错误:
io.ReadAll(resp.Body)后直接打印,但没检查err—— 网络错误、解码失败、gzip 解压异常都可能在这里爆发
解析 HTML 推荐用 goquery,别手撕正则
Go 原生 html 包能解析 DOM,但写选择器太 verbose;goquery 提供 jQuery 风格语法,开发效率高,且底层仍用原生 html 包,安全可靠。
- 安装:
go get github.com/PuerkitoBio/goquery - 加载 HTML:用
goquery.NewDocumentFromReader()接收io.Reader(比如resp.Body),避免先读成字符串再转strings.NewReader - 选择器写错不会 panic,而是返回空集合 —— 所以每次
.Each()或.Text()前建议加.Size() > 0判断 - 注意编码:如果网页是 GBK 或 GB2312,
goquery默认按 UTF-8 解析会乱码;需先用golang.org/x/net/html/charset检测并转换
并发控制不能只靠 go 关键字
盲目对每个 URL 启 goroutine,很容易触发连接数限制、被封 IP,或耗尽文件描述符。
- 用带缓冲的 channel 控制并发数,例如
sem := make(chan struct{}, 5),每次请求前sem ,结束后 - 别用
time.Sleep模拟限速 —— 它阻塞整个 goroutine;应使用time.After或更稳妥的 token bucket(如golang.org/x/time/rate) - HTTP 复用很重要:
http.Client的Transport应启用连接池(默认已开),但要调小MaxIdleConns和MaxIdleConnsPerHost,避免打爆目标服务器 - 日志里别打完整 URL(含参数),可能泄露敏感信息;用
url.URL{Scheme: u.Scheme, Host: u.Host, Path: u.Path}截断后再记录
Robots.txt 和 User-Agent 不是可选项
绕过 robots.txt 或伪造 UA 不仅违反爬虫伦理,还常导致 403、429 或直接 TCP RST。合规性是长期稳定运行的前提。
立即学习“go语言免费学习笔记(深入)”;
- 请求前先 GET
/robots.txt,用golang.org/x/net/robotstxt解析,检查txt.TestAgent(path, "your-bot") -
User-Agent必须设为真实、可追溯的值(如"my-crawler/1.0 (+https://example.com/bot)"),不能用浏览器 UA 混淆身份 - 有些站点通过 JS 渲染内容,纯 HTTP + goquery 拿不到数据 —— 这时候不是爬虫写得不对,而是该换方案(如
chromedp),但性能和资源开销会显著上升
真正难的不是发请求或选选择器,而是判断什么时候该停、什么时候该退、什么时候该换策略。网络不稳定、页面结构突变、反爬升级,这些都不会报编译错误,但会让爬虫在凌晨三点静默失败。










