golangの日記

Go言語を中心にプログラミングについてのブログ

Go言語(golang) net/htmlを使ったHTMLのパース

golang.png


net/htmlのドキュメントは以下
https://pkg.go.dev/golang.org/x/net/html?tab=doc

goquery(goでjQueryライクにHTMLを扱えるパッケージ)
https://github.com/PuerkitoBio/goquery
https://pkg.go.dev/github.com/PuerkitoBio/goquery?tab=doc
内部的には net/html と cascadia が使われている





goqueryを使えば簡単だけど、例えばアンカータグのhref属性のみ取得する
というような場合であれば net/htmlでもそんなに面倒じゃない。

package main

import (
    "bytes"
    "fmt"
    "log"
    "strings"

    "golang.org/x/net/html"
    "golang.org/x/net/html/atom"
)

type Anchor struct {
    Text string
    Href string
}

func newAnchor(node *html.Node) *Anchor {
    var buff bytes.Buffer
    // A要素のテキストを取得
    for c := node.FirstChild; c != nil; c = c.NextSibling {
        if c.Type == html.TextNode {
            buff.WriteString(c.Data)
        }
    }

    // href属性の値を取得
    href := ""
    for _, v := range node.Attr {
        if v.Key == "href" {
            href = v.Val
            break
        }
    }

    return &Anchor{Text: buff.String(), Href: href}
}

func findAnchors(node *html.Node, collection *[]*Anchor) {
    for c := node.FirstChild; c != nil; c = c.NextSibling {
        if c.Type == html.ElementNode {
            if c.DataAtom == atom.A {
                *collection = append(*collection, newAnchor(c))
            }
            findAnchors(c, collection)
        }
    }
}

func main() {

    r := strings.NewReader(`
<html>
<head></head>
<body>
  <ul>
      <li><a href="https://example.com/foo">foo</a></li>
      <li><a href="https://example.com/bar">bar</a></li>
      <li><a href="https://example.com/baz">baz</a></li>
  </ul>
</body>
</html>
`)

    node, err := html.Parse(r)
    if err != nil {
        log.Fatal(err)
    }

    var collection []*Anchor
    findAnchors(node, &collection)

    for _, a := range collection {
        fmt.Println(a.Text, ":", a.Href)
    }
}