如何在Golang中解析HTML页面_Golang net/html HTML解析方法

必须先调用 html.Parse() 或 html.ParseFragment() 构建 DOM 树,不可直接操作 html.Node;Parse 用于完整 HTML 文档,需 io.Reader 输入;ParseFragment 适用于 HTML 片段。

net/html 解析 HTML 时,必须先调用 html.Parse() 获取根节点

直接对原始 HTML 字符串调用 html.Node 相关操作会 panic,因为没有解析器上下文。必须先通过 html.Parse()html.ParseFragment() 构建 DOM 树。

  • html.Parse() 用于完整 HTML 文档(含 等),输入需是 io.Reader,常见用法:
    doc, err := html.Parse(strings.NewReader(htmlStr))
    if err != nil {
        log.Fatal(err)
    }
  • html.ParseFragment() 适合解析片段(如仅 ...),需传入一个“上下文节点”(如 &html.Node{Type: html.ElementNode, Data: "body"}),否则可能丢节点或 panic
  • 注意:解析器不校验编码,默认按 UTF-8 处理;若 HTML 声明为 gbkutf-8 但实际字节非 UTF-8,会读出乱码甚至提前截断

遍历节点时,node.Typenode.Data 更可靠

很多新手误以为查 node.Data == "div" 就能定位元素,但 Data 在不同 Type 下含义不同:文本节点存内容,注释节点存注释文字,元素节点才存标签名。漏判 Type 会导致逻辑错位或空指针。

  • 关键类型常量:html.ElementNode(标签)、html.TextNode(文本)、html.CommentNode(注释)、html.DoctypeNode(doctype)
  • 安全遍历示例:
    func findDivs(n *html.Node) {
        if n.Type == html.ElementNode && n.Data == "div" {
            // 处理 div 元素
            fmt.Println("found div")
        }
        for c := n.FirstChild; c != nil; c = c.NextSibling {
            findDivs(c)
        }
    }
  • 不要依赖 n.DataAtom —— 它是内部优化字段,未导出且仅在特定构建下有效,跨平台或新版 Go 可能失效

node.Attr 是切片,查属性必须遍历,没有 GetAttribute() 这种方法

Go 的 net/html 不提供类似 jQuery 的 attr("href") 便捷接口,所有属性访问都得手动循环 node.Attr 切片比对 Key

  • 常见错误:写成 node.Attr["href"] —— 编译失败,因为 Attr[]html.Attribute 类型,不是 map
  • 正确写法:
    func getHref(n *html.Node) (href string) {
        for _, a := range n.Attr {
            if a.Key == "href" {
                href = a.Val
                break
            }
        }
        return href
    }
  • 注意大小写:HTML 属性名不区分大小写,但 net/html 不做归一化,"HREF""href" 被视为不同 key;生产环境建议统一转小写再比较

解析后不调用 html.Render() 就无法还原 HTML 字符串

如果你从网页提取结构后想拼回 HTML 片段(比如清洗后重新生成),不能靠字符串拼接,必须用 html.Render() 写入 io.Writer。否则丢失转义、闭合逻辑、自闭合标签处理(如 )等细节。

  • html.Render() 会自动处理特殊字符转义(如 zuojiankuohaophpcn)、标签闭合、空白压缩策略(取决于节点类型)
  • 简单还原示例:
    var buf bytes.Buffer
    err := html.Render(&buf, doc)
    if err != nil {
        log.Fatal(err)
    }
    htmlOut := buf.String() // 此时才是合法可渲染的 HTML
  • 注意:若只取部分子树(如某个 ),要确保其作为独立片段可渲染 —— 比如 子节点单独 render 可能缺 ,浏览器容错强,但服务端处理时需留意 解析 HTML 最容易被忽略的是编码推断和错误恢复机制:net/html 在遇到非法嵌套(如

    )时会自动修复 DOM 结构,这和浏览器一致,但也意味着你写的 XPath 或层级假设可能在意外情况下失效。