Goのgolang.org/x/net/htmlパッケージでBOM付きUTF-8にやられた話

複数のhtmlファイルに対して、特定のhtml要素を差し込んで保存するのにgolang.org/x/net/html使ってDOM操作すればいける!と思って実装していると、一部htmlでDOMをRenderした結果がおかしくなった。

具体的にはこんなコードで

package main

import (
	"os"

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

func main() {
	f, _ := os.Open("./index.html")
	defer f.Close()
	doc, _ := html.Parse(f)
	html.Render(os.Stdout, doc)
}

こんなhtmlを

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>こんなhtml</title>
</head>
<body>
  It Works.
</body>
</html>

ParseしてRenderした結果が

<html lang="ja"><head></head><body>
  <meta charset="utf-8"/>
  <title>こんなhtml</title>
  It works.
</body></html>

こんなん。head要素にあったものが全てbody内に入ってしまう、という状態。

ググってもそもそもgolang.org/x/net/htmlの事例が少ないし、オリジナルのhtmlから少しずつ行を削除して試してを繰り返すも状況変わらないし、途方にくれているところでふとfileコマンドを実行してみると

$ file index.html 
index.html: HTML document text, UTF-8 Unicode (with BOM) text, with very long lines, with CRLF line terminators

タイトル通りですが、BOMのせいでした。以下のような関数を用意して、あらかじめBOMを除去した上でパースすればOKでした。

func stripBOM(b []byte) []byte {
	stripped := bytes.Trim(b, "\xef\xbb\xbf")
	return stripped
}

参考