goquery.newdocumentfromreader必须用bufio.newreader包装resp.body以防空文档或乱码;需先检查statuscode,gbk页面须用charset.newreader转码;find返回惰性selection,each才遍历,链式调用前须判空。

goquery.NewDocumentFromReader 传参必须带 bufio.NewReader
直接把 resp.Body 丢给 goquery.NewDocumentFromReader 容易出空文档或乱码,尤其在 Body 已被其他逻辑读过一次时。底层 net/html 解析器对 reader 的 buffer 敏感,不包装就可能提前 EOF。
- 务必用
bufio.NewReader(resp.Body)包一层再传入 - 如果网页声明
charset=gbk,得先用golang.org/x/net/html/charset.NewReader转码,否则中文全变??? -
resp.StatusCode必须在http.Get后立刻检查,403或404时NewDocumentFromReader仍会返回非 nil 的 doc,但里面没节点
Find() 不执行查询,Each() 才真正遍历
Find("a") 返回的是一个惰性 *goquery.Selection,它只记下选择器,不查 DOM。很多人写 doc.Find("a").Text() 以为能拿到所有链接文本,结果只是第一个匹配项的合并文本(含子元素内容)。
- 要取全部链接文本,必须用
Each()或EachWithIndex() -
Find("div.title").Attr("data-id")如果没匹配到,返回空字符串,不 panic —— 但后续链式调用如.Children().Eq(0)会 panic - 安全写法:先判长度
if s := doc.Find("a"); s.Length() > 0 { ... }
net/html 原生解析需手动递归提取文本
用 html.Parse() 构建 DOM 树后,node.Data 不等于“文本内容”——只有 node.Type == html.TextNode 时才该取 node.Data。常见错误是直接比 node.Data == "p",其实那是标签名,得先判断 node.Type == html.ElementNode。
- 提取某元素全部可见文本,得递归遍历子树,收集所有
html.TextNode的Data - 不要用
node.DataAtom,它是未导出内部字段,跨 Go 版本可能失效 -
node.Attr是切片,查href得自己循环:for _, a := range node.Attr { if a.Key == "href" { ... } }
LoadHTMLString 对编码和格式极其敏感
goquery.LoadHTMLString 看似方便,但输入稍有异常就会静默失败:含 BOM、开头是 <?xml 、标签未闭合、非 UTF-8 编码,都可能导致返回空 *Selection 或 panic。
- 确保字符串是纯 UTF-8,可用
strings.TrimPrefix(htmlStr, "\xef\xbb\xbf")去 BOM - 遇到
<?xml version="1.0" encoding="GBK"?>这种,得先strings.TrimSpace()再strings.SplitN(..., "\n", 2)[1]掉首行 - 严重破损 HTML(比如嵌套
<div> 没闭合),别硬塞给 <code>LoadHTMLString,改用NewDocumentFromReader+charset.NewReader更稳 实际跑通的关键不在选库,而在每一步的 reader 控制、编码探测和空值防御——这些地方一漏,程序就“看起来没报错,但数据全空”。











