go 语言需用 goquery 解析 html,关键在正确将 resp.body 传给 newdocumentfromreader 并处理编码;find 延迟计算,each 才遍历;动态页面优先调 api,反爬需设 header,连接与超时须显式配置。

Go 语言本身不内置 HTML 解析能力,net/http 只负责发请求拿回原始 HTML 字符串,真正提取数据得靠第三方库——最常用、最稳的是 goquery,它模仿 jQuery API,上手快,但必须配合 net/html 底层解析器工作。
用 goquery 解析响应 HTML 的标准流程
关键不是“怎么发请求”,而是“怎么把 http.Response.Body 正确喂给 goquery.NewDocumentFromReader”。常见错误是直接传 resp.Body 而没考虑字符编码或 Body 已被读取过。
- 务必在
http.Get后立即检查resp.StatusCode,403/404 会返回空文档但无报错 - 如果网页含
charset=gbk或gb2312,需先用golang.org/x/net/html/charset转码,否则中文变??? -
goquery.NewDocumentFromReader接收io.Reader,推荐包装成bufio.NewReader(resp.Body)防止底层 buffer 不足 - 示例片段:
doc, err := goquery.NewDocumentFromReader(bufio.NewReader(resp.Body))
Find() 和 Each() 的实际边界在哪里
Find() 返回的是 *goquery.Selection,它不立刻执行 DOM 查询,而是延迟计算;Each() 才真正遍历匹配节点。很多人误以为 Find("a").Text() 能取所有链接文本——其实只取第一个匹配项的合并文本。
- 要取全部
<a></a>的href,得写doc.Find("a").Each(func(i int, s *goquery.Selection) { href, _ := s.Attr("href") }) -
Text()会拼接所有子文本(含注释、script 内容),如需干净文本,建议用s.Find("p").Contents().FilterFunction(...)过滤 - 选择器支持大部分 CSS3,但不支持
:nth-child(2n)这类伪类,可用Filter()+ 自定义逻辑替代
处理动态渲染页面:别硬刚 JS
如果目标网页依赖 JavaScript 渲染内容(比如 React/Vue SPA),goquery 拿到的只是初始 HTML,document.querySelectorAll 能看到的数据它看不到。
- 先确认是否真需要执行 JS:查看 Network 面板,找 XHR/Fetch 请求,直接调用 API 更快更稳
- 真要渲染,得上
chromedp,但它启动 Chrome 实例、内存开销大、部署麻烦,别为简单爬虫引入 - 部分网站反爬会校验
User-Agent+Accept-Encoding+Referer,缺一可能返回 403 或空数据,http.Client要手动设Header
最易被忽略的是 HTTP 连接复用和超时控制:http.DefaultClient 默认不限制连接数,爬多页时容易触发系统文件描述符耗尽;Timeout 不设的话,DNS 卡住或服务器失联会让整个程序挂住。这些不写在 goquery 文档里,但决定你脚本能跑通还是跑崩。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











