2023-06-19
こんにちは。Goの話です。
net/htmlでHTMLをパースするとき要素の特定に四苦八苦しました
net/htmlでHTMLをパースする
HTMLのパースには golang.org/x/net/html を使います。例えば次のように使います。
import ( "fmt" "strings" "golang.org/x/net/html" ) func main() { // io.Reader へ r := strings.NewReader(`<html><head></head><body></body></html>`) node, _ := html.Parse(r) fmt.Printf("%v\n", node.Type == html.DocumentNode) // true fmt.Printf("%v\n", node.FirstChild.Data) // html }
html.Parse すると Node が返ります。
要素を取得する
シンプルなHTMLです。各要素を取得してみます
r := strings.NewReader(` <html><head></head><body><a href="/" class="text-lg w-24">cc</a></body></html> `) node, _ := html.Parse(r) fmt.Printf("%v\n", node.FirstChild.Data) // html fmt.Printf("%v\n", node.FirstChild.FirstChild.Data) // head bodyElm := node.FirstChild.FirstChild.NextSibling // head の次の要素を取得 fmt.Printf("%v\n", bodyElm.Data) // body
ポイントは FirstChild と NextSibling です
- FirstChild で、一番初めの子要素を取得できます
- NextSibling で、次の要素(兄弟)を取得できます
属性を取得する
class や href を取得します
r := strings.NewReader(` <html><head></head><body><a href="/" class="text-lg w-24">cc</a></body></html> `) node, _ := html.Parse(r) bodyElm := node.FirstChild.FirstChild.NextSibling // head の次の要素を取得 aElm := bodyElm.FirstChild fmt.Printf("%v\n", aElm.Data) // a fmt.Printf("%v\n", aElm.Attr[0].Key) // href fmt.Printf("%v\n", aElm.Attr[0].Val) // / fmt.Printf("%v\n", aElm.Attr[1].Key) // class fmt.Printf("%v\n", aElm.Attr[1].Val) // text-lg w-24
属性は Attr で取得できます。
ちなみにdata属性も同じように取得できました。
終わりに
FirstChild や NextSibling は DOM の Node にインタフェースが似ているようです。
HTMLが複雑なときは goquery など、よりレイヤーの高いAPIと組み合わせて使ったほうがいいかもしれません。
作成日
2023-06-19
更新日
2023-07-10