import "html"

HTML5に対応したトークナイザー(トークン分割)とパーサーを実装しています。

トークン化は、io.Readerであるr用のTokenizerを作成することによって行われます。呼び出し側は、このrから得られるデータが、確実にUTF-8でエンコードされたHTMLであることに責任を持たなくてはなりません。

z := html.NewTokenizer(r)

Tokenizer zに対し、z.Next()を繰り返し呼び出すことでHTMLはトークン化されます。Nextは、次のトークンを解析し、その型およびエラーを返します。

for {
	tt := z.Next()
	if tt == html.ErrorToken {
		// ...
		return ...
	}
	// カレントのトークンを処理する
}

カレントのトークンを得るためのAPIが、2種類用意されています。高レベルのAPIでは、Tokenを呼び出します。低レベルのAPIでは、TextまたはTagName/TagAttrを呼び出します。両APIともに、Nextのあと必要に応じてRawを呼び出せますが、Rawは、Token、Text、TagName、TagAttrより前に呼び出します。各トークン毎に、有効な呼び出しシーケンスを下にEBNF表記法で示します。

Next {Raw} [ Token | Text | TagName {TagAttr} ]

Tokenは、ひとつのトークンを完全に表した、独立したデータ構造を返します。実体(例えば「<」)はアンエスケープされ、タグ名と属性のキーは小文字に変換され、各属性は[]Attributeに収集されます。下は例です。

for {
	if z.Next() == html.ErrorToken {
		// os.EOFが返されたときは成功したことを示す
		return z.Error()
	}
	emitToken(z.Token())
}

低レベルのAPIの方は、アロケートとコピー量は少ないのですがText、TagName、TagAttrから返される[]byte値の内容は、次のNextの呼び出しで入れ替わってしまいます。次の例では、HTMLページのアンカーテキストを抽出しています。

depth := 0
for {
	tt := z.Next()
	switch tt {
	case ErrorToken:
		return z.Error()
	case TextToken:
		if depth > 0 {
			// emitBytesでは、受け取った[]byteをすぐに処理しないときは、
			// コピーしておかなければなりません。
			emitBytes(z.Text())
		}
	case StartTagToken, EndTagToken:
		tn, _ := z.TagName()
		if len(tn) == 1 && tn[0] == 'a' {
			if tt == StartTag {
				depth++
			} else {
				depth--
			}
		}
	}
}

パースは、io.ReaderとともにParseを呼び出すことで行われます。Parseは、解析したツリーのルート(document要素)を*Nodeとして返します。呼び出し側は、このReaderから得られるデータが、確実にUTF-8でエンコードされたHTMLであることに責任を持たなくてはなりません。次の例では、ネストの浅い順に各アンカーノードを処理しています。

doc, err := html.Parse(r)
if err != nil {
	// ...
}
var f func(*html.Node)
f = func(n *html.Node) {
	if n.Type == html.ElementNode && n.Data == "a" {
		// nを使って何か行う...
	}
	for _, c := range n.Child {
		f(c)
	}
}
f(doc)

これらに関連する仕様については、次を参照ください: http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.htmlhttp://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html

パッケージファイル

doc.go entity.go escape.go parse.go token.go

EscapeString関数

func EscapeString(s string) string

EscapeStringは、「<」が「&lt;」となるように特殊文字をエスケープします。この関数がエスケープするのは、次の5文字だけです。(amp、apos、lt、gt、quot) UnescapeString(EscapeString(s)) == sは、常に成立しますが、逆は必ずしも成り立ちません。

UnescapeString関数

func UnescapeString(s string) string

UnescapeStringは、「&lt;」が「<」となるように実体をアンエスケープします。この関数は、EscapeStringがエスケープを行うより、より広い範囲で実体のアンエスケープを行います。例えば、「&aacute;」は「á」にアンエスケープします。同様に「&#225;」、「&xE1」も行えます。UnescapeString(EscapeString(s)) == sは、常に成立しますが、逆は必ずしも成り立ちません。

Attribute型

Attributeは、属性のキーと値の組み合わせです。Keyは、アルファベット(故に、エスケープ対象の文字、「&」、「<」、「>」などは含められません)であり、Valはアンエスケープ(「a&lt;b」でなく「a<b」というように)されています。

type Attribute struct {
    Key, Val string
}

Node型

Nodeは、NodeTypeといくつかのData(要素ノードではタグ名、テキストでは内容)から成り、ノードツリーの一部でもあります。要素ノードは、Attributeのスライスを持つことがあります。Dataは、アンエスケープされているため、「a&lt;b」でなく「a<b」となります。

type Node struct {
    Parent *Node
    Child  []*Node
    Type   NodeType
    Data   string
    Attr   []Attribute
}

Parse関数

func Parse(r io.Reader) (*Node, os.Error)

Parseは、与えたReaderからHTMLを解析し、ツリー構造を返します。この入力はUTF-8エンコードされているとみなされます。

NodeType型

NodeTypeは、ノードの型です。

type NodeType int
const (
    ErrorNode NodeType = iota
    TextNode
    DocumentNode
    ElementNode
    CommentNode
)

Token型

Tokenは、TokenTypeといくつかのData(開始タグ・終了タグではタグ名、テキストでは内容)から成ります。タグのTokenは、Attributeのスライスを持つことがあります。タグおよびテキストのTokenのDataは、アンエスケープされているため、「a&lt;b」でなく「a<b」となります。

type Token struct {
    Type TokenType
    Data string
    Attr []Attribute
}

(Token) String関数

func (t Token) String() string

Stringは、このTokenの文字列表現を返します。

TokenType型

TokenTypeは、Tokenの型です。

type TokenType int
const (
    // ErrorTokenは、トークン化の際にエラーが起きたことを意味します。
    ErrorToken TokenType = iota
    // TextTokenは、テキストノードを意味します。
    TextToken
    // StartTagTokenとは、<a>などです。
    StartTagToken
    // EndTagTokenとは、</a>などです。
    EndTagToken
    // SelfClosingTagTokenとは、<br/>などです。
    SelfClosingTagToken
)

(TokenType) String関数

func (t TokenType) String() string

Stringは、このTokenTypeの文字列表現を返します。

Tokenizer型

Tokenizerは、HTMLのトークンのストリームを返します。

type Tokenizer struct {
    // contains unexported fields
}

NewTokenizer関数

func NewTokenizer(r io.Reader) *Tokenizer

NewTokenizerは、指定したReaderの新しいHTML Tokenizerを返します。この入力はUTF-8エンコードされているとみなされます。

(*Tokenizer) Error関数

func (z *Tokenizer) Error() os.Error

Errorは、直近のErrorTokenとなったトークンのエラーを返します。これがos.EOFを返すときは通常、トークン化が終了したことを示しています。

(*Tokenizer) Next関数

func (z *Tokenizer) Next() TokenType

Nextは、次のトークンを走査し、その型を返します。

(*Tokenizer) Raw関数

func (z *Tokenizer) Raw() []byte

Rawは、カレントのトークンの、何も手を加えられていない生のテキストを返します。返されるスライスの内容は、Next、Token、Text、TagName、TagAttrのいずれかを呼び出すと変更される可能性があります。

(*Tokenizer) TagAttr関数

func (z *Tokenizer) TagAttr() (key, val []byte, remaining bool)

TagAttrは、カレントのタグトークン内の、まだパースされていない次の属性が持つ、小文字へ変換したキー、アンエスケープした値、さらに属性が存在するかどうかを返します。返されるスライスの内容は、次にNextを呼び出すと変更される可能性があります。

(*Tokenizer) TagName関数

func (z *Tokenizer) TagName() (name []byte, remaining bool)

TagNameは、タグトークンの小文字へ変換したキー(「<IMG SRC=”foo”>」だと「img」)と、このタグが属性を持つかどうかを返します。返されるスライスの内容は、次にNextを呼び出すと変更される可能性があります。

(*Tokenizer) Text関数

func (z *Tokenizer) Text() []byte

Textは、アンエスケープ後の生データを返します。返されるスライスの内容は、次にNextを呼び出すと変更される可能性があります。

(*Tokenizer) Token関数

func (z *Tokenizer) Token() Token

Tokenは、次のTokenを返します。返されたToken内のDataとAttrの値は、以降にNextを呼び出した後も有効なままです。