安全发GET请求需显式构造*http.Client,设Timeout=10秒、Transport连接池参数(MaxIdleConns等≥50)、User-Agent头,defer resp.Body.Close();中文乱码应优先用golang.org/x/net/html/charset.NewReaderLabel()自动识别编码。

直接上手写 Golang 爬虫,核心就三步:发请求、解编码、提数据。跳过框架、不碰 JS 渲染,用 net/http + goquery 就能跑通 90% 的静态新闻/商品/列表页。
怎么用 http.Client 发出安全的 GET 请求
别用 http.Get() —— 它背后是 http.DefaultClient,没超时、没连接复用、没 UA,一并发就卡死或被秒封。
- 显式构造
*http.Client,必须设Timeout(建议 10 秒)和Transport参数 -
MaxIdleConns和MaxIdleConnsPerHost至少设为 50,否则高频请求会反复建连、触发too many open files - 每条请求都加
User-Agent头,值用真实浏览器标识,比如"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" - 记得
defer resp.Body.Close(),漏掉这句,爬几百页后程序大概率崩在文件描述符耗尽
client := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 50,
MaxIdleConnsPerHost: 50,
IdleConnTimeout: 30 * time.Second,
},
}
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64)")
resp, err := client.Do(req)中文乱码怎么解?别信 Content-Type 和 meta charset
很多新闻站 HTML 里写 <meta charset="gb2312">,但响应头却是 Content-Type: text/html(没 charset),goquery 不会自动按 meta 转码,直接 string(body) 就是乱码。
- 优先用
golang.org/x/net/html/charset的charset.NewReaderLabel(),它会同时检查响应头和 HTML meta - 对已知站点(比如某地方新闻网),直接按 GB18030 解码更稳:
simplifiedchinese.GB18030.NewDecoder().Bytes(body) - 别用
io.ReadAll(resp.Body)后再转码——大页面会吃光内存;应传入charset.NewReaderLabel(resp.Body, ...)给goquery.NewDocumentFromReader
用 goquery 提取标题和链接时常见翻车点
goquery 写法像 jQuery,但几个细节不注意就会空指针 panic 或提取错内容:
立即学习“go语言免费学习笔记(深入)”;
-
Attr("href")返回(string, bool),必须检查第二个返回值:if href, ok := s.Attr("href"); ok { ... } -
Find("a")匹配所有<a>,但Text()会把子标签文本全拼起来;要精确取标题,用Find(".news-title").First().Text() - 相对 URL(如
/article/123)不能直接Visit,得用url.Join(baseURL, href)转成绝对地址 - 如果目标元素是 JS 渲染的(右键“查看网页源代码”搜不到关键词),
goquery拿不到,别硬试——换 API 或放弃
并发抓取时最该盯住的两个变量
开 goroutine 很容易,但并发数和请求间隔不控,轻则 429 被限流,重则本地端口占满、DNS 查询阻塞。
- 用
sem := make(chan struct{}, 3)控制并发上限(先从 3 开始试),每个 goroutine 开始前sem <- struct{}{},结束后<-sem - 对同一域名,用
time.Since(lastReqTime) < 1 * time.Second强制间隔,比全局time.Sleep更准——毕竟你可能同时抓多个站 - 遇到
429 Too Many Requests或503 Service Unavailable,立刻停发、指数退避重试(比如 1s → 2s → 4s),别重试三次就放弃
真正难的不是写出来,而是让爬虫在不同网站上稳定活过一星期:UA 要轮换、URL 去重要用 sync.Map、错误日志得带原始 URL 和状态码——这些细节不补,跑两天就断在奇怪的地方。


















