Go基础爬虫核心是net/http发请求+io.ReadAll/流式读取+goquery解析HTML;需设超时Client、关闭resp.Body、处理编码乱码、注意goquery大小写及命名空间、添加User-Agent/Referer、随机延时与重试。

Go 语言写基础爬虫,核心就是 net/http 发请求 + io.ReadAll 或流式读取响应体 + 第三方库(如 goquery)解析 HTML。不依赖重量级框架,几行代码就能跑起来,但容易在重定向、编码、超时、User-Agent 和反爬响应上翻车。
用 http.Get 发最简请求,但必须设超时
直接调 http.Get 看似简单,但它底层用的是默认的 http.DefaultClient,没有超时控制——遇到网络卡顿或目标不响应,goroutine 会永久阻塞。
正确做法是构造带超时的 *http.Client:
client := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := client.Get("https://example.com")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()常见错误:漏掉 resp.Body.Close(),导致 TCP 连接不释放,爬多了直接触发 too many open files。
立即学习“go语言免费学习笔记(深入)”;
处理中文乱码:别信 Content-Type 的 charset
很多网站 HTML 中声明了 <meta charset="gb2312">,但响应头里的 Content-Type 却是 text/html; charset=utf-8,或者干脆没声明。Go 的 http.Response 不自动按 meta 标签转码。
实操建议:
- 先用
charset.NewReaderLabel(来自golang.org/x/net/html/charset)尝试按 HTTP 头或 HTML meta 推断编码 - 更稳的方式是用
github.com/saintfish/chardet或golang.org/x/text/encoding手动检测并转换 - 对已知站点,直接按预期编码解码,比如:
body, _ := io.ReadAll(resp.Body) utf8Body := toUTF8(body, "gbk") // 自定义函数,用 iconv 或 golang.org/x/text/encoding/simplifiedchinese.GB18030.Decode
用 goquery 解析 HTML,注意选择器大小写和命名空间
goquery 是 jQuery 风格的 HTML 解析器,但它是基于 Go 的 net/html 构建的,对 malformed HTML 容错不如浏览器,且严格区分大小写。
典型坑点:
-
doc.Find("div.title")匹配不到?检查原始 HTML 里 class 是title还是Title或data-title - XML 命名空间标签(如
<rss:channel>)需用doc.Find("rss\:channel"),反斜杠要双写 - 链式调用中一旦
.Find()没匹配到元素,后续.Text()返回空字符串,不会 panic,但容易掩盖逻辑错误
示例:
doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
if err != nil {
log.Fatal(err)
}
doc.Find("h1").Each(func(i int, s *goquery.Selection) {
title := strings.TrimSpace(s.Text())
fmt.Println(title)
})绕过基础反爬:Header、Referer、重试与随机延时不能少
多数静态页面只要加个 User-Agent 就能访问,但稍严一点的站点会校验 Referer、拒绝短时间高频请求,甚至返回 403 或空白内容。
最小可行组合:
- 设置
User-Agent:模仿 Chrome 最新版,避免用Go-http-client/1.1 - 加
Referer:比如从首页跳转过来,就填首页 URL - 每次请求前
time.Sleep(1 * time.Second),或用rand.Intn(500)+500毫秒随机延时 - 失败时重试 1–2 次,用指数退避(
time.Sleep(time.Second )
别硬刚 JS 渲染页——那是 chromedp 或无头浏览器的事,基础爬虫就盯住纯 HTML + API 接口。
真正麻烦的从来不是“怎么发请求”,而是“怎么让对方相信你是人而不是脚本”,而这个边界,往往藏在 Cookie、Token、加密参数或行为序列里——那已经超出 http.Get 能解决的范围了。










