0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Typescript で 簡単な HTML Parser を書いてみた

Posted at

メモ。
間違いなどありましたら、ご指摘お願いします

簡単な HTML Parser を書いてみた

HTML Parser の説明は、こちらなどを参考にしてください。
ファイルは、githubにあります。

現状は、空白が完全に除去された HTML ファイル (<p><div>hoge</div></p> のような形式 ) で SelfClosingタグ (<img /><br /> のように、閉じるタグがないが / で閉じているタグ) を認識できる形になっています (コメントアウトは実装していません) 。

では、簡単にコードを説明します。

説明 (型)

type MyHTMLElement = {
	nodeName: string;
	childrenNode: MyHTMLElement[];
	parentNode: MyHTMLElement | undefined;
	textContent: string;
	attributes: MyAttribute | undefined;
	DocumentPosition: MyDocumentPosition | undefined;
	isSelfClosingTag: boolean;
}

type MyAttribute = {
	[key in string]: string
}

type MyDocumentPosition = {
	openTag: { start: number | undefined; end: number | undefined; }
	closeTag: { start: number | undefined; end: number | undefined; }
}

前の記事 の型説明は分かりづらかったと思うんですが、今回は nodeName, childrenNode, parentNode などがあって、分かりやすい形になっていると思います。

説明 (実装)

実装に当たっては、ここのコードにある、正規表現を使いました。

let domParserTokenizer = /(?:<(\/?)([a-zA-Z][a-zA-Z0-9\:]*)(?:\s([^>]*?))?((?:\s*\/)?)>)/gm;
// 1 : / 2 : tag_name 3 : attribute 4 : /
let splitAttrsTokenizer = /([a-z0-9_\:\-]*)\s*?=\s*?(['"]?)(.*?)\2\s+/gim
// 1 : name 2 : "' 3 : value 4 : 2

1つ目の domParserTokenizer は <div> などのタグを認識し、2つ目の splitAttrsTokenizer は class="hoge" などの attribute を認識します。

パースする方法は、まず上の正規表現でマッチした部分を while文で回し、各ループで 上で書いた型に沿って HTMLElement を定義していく方法です。

その時の最初の parentNode になるのが、

	let parent: MyHTMLElement = {
		nodeName: "document",
		childrenNode: [],
		parentNode: undefined,
		textContent: "",
		attributes: undefined,
		DocumentPosition: undefined,
		isSelfClosingTag: false
	}

の部分です。

では、while文の中身を見てみましょう。
まず、

		if (token[1] !== "/") {

からの条件式では、通常の <div> のように / で区切られていないタグを HTMLElement として登録します。
そして、<img /> のように 自分で閉じた形のタグの場合は、closeTag を設定し、それ以外の場合は parent をそのタグに変更します。

			if (token[4] && token[4].indexOf("/") != -1 && tag.DocumentPosition != undefined) {
				tag.DocumentPosition.closeTag = tag.DocumentPosition.openTag
				tag.isSelfClosingTag = true
			} else {
				parent = tag
			}

では、次に </div> のように / で区切られたタグ を見てみましょう。条件式は以下のようになっています。

		} else {
			if (token[2].toLowerCase() == parent.nodeName) {

ここでは、tag.DocumentPosition で抜けている closeTag を追加しています。

これで、なんとか 簡単な HTML Parser を実装できました。
ヒントを多く与えてくれたコード には感謝です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?