コンテンツにスキップ

DomController / XmlNode

$parser.xml()$parser.html()は、文字列をDOMとして解析し、XPathでノードを検索できるDomControllerを返します。

DOMは、XMLやHTMLを「ノードのツリー」として扱うための形式です。タグは要素ノード、タグの中の文字はテキストノードとして管理されます。

Trace Kernelでは、まず$parser.xml()または$parser.html()で文字列をDomControllerに変換し、query()で必要なノードを探します。見つかったノードはXmlNodeとして扱い、text()attr()で値を取り出します。

const dom = await $parser.html($resource.pageHtml);
const titles = await dom.query('//title');
for (const title of titles) {
$println(await title.text());
}
await dom.dispose();

DOM操作は、次の流れで使います。

  1. 文字列をDomControllerへ変換する
  2. query(xpath)で必要なノードを探す
  3. 見つかったXmlNodeからテキストや属性を取り出す
  4. 最後にdispose()でDOMを破棄する
const html = `<html>
<body>
<h1>Trace Kernel</h1>
<a href="/download/">Download</a>
</body>
</html>`;
const dom = await $parser.html(html);
const links = await dom.query('//a');
for (const link of links) {
const text = await link.text();
const href = await link.attr('href');
$println(`${text}: ${href ?? ''}`);
}
await dom.dispose();

text()attr()は非同期メソッドです。呼び出すときはawaitします。

$parser.xml()はXMLとして厳密に解析します。タグの閉じ忘れなど、XMLとして不正な文字列はエラーになります。

const xml = `<users>
<user id="001">taro</user>
<user id="002">jiro</user>
</users>`;
const dom = await $parser.xml(xml);

$parser.html()はHTMLとして解析します。Webページや保存済みHTMLから、リンク、見出し、表などを取り出す用途に向いています。

const dom = await $parser.html($resource.pageHtml);

query()にはXPath文字列を渡します。Trace KernelのDOM APIは、基本的な抽出に必要な範囲のXPathをサポートしています。

XPath意味
//a文書全体からa要素をすべて探します。
/html/body/h1ルートから順にhtmlbodyh1をたどります。
//*文書全体からすべての要素を探します。
//a[@href]href属性を持つa要素を探します。
//div[@class="item"]class属性がitemdiv要素を探します。

対応している主な構文は次の通りです。

  • / による子要素の指定
  • // による子孫要素の指定
  • 要素名による検索
  • * による任意要素の検索
  • [@name] による属性の存在チェック
  • [@name="value"] による属性値の一致チェック

複雑なXPath関数、テキスト条件、数値条件、名前空間を使った検索などは、現時点では対象外です。

メソッド戻り値説明
root()Promise<XmlNode | null>ルートノードを取得します。
query(xpath)Promise<XmlNode[]>XPathでノードを検索します。
debug()Promise<{ domId: number; nodeCount: number }>DOM内部情報を取得します。
dispose()Promise<void>DOMを破棄します。

query()は文書全体を起点に検索します。XML/HTMLの中から目的の要素を直接取り出したい場合は、まずdom.query('//tagName')を使うのが基本です。

root()はルート要素を取得します。ルート要素から順に子要素をたどりたい場合に使います。

const root = await dom.root();
if (root) {
$println(await root.name() ?? '');
}

debug()は、DOM内部のIDとノード数を確認するための補助機能です。通常の解析処理では必須ではありません。

dispose()は、Rust側に保持されているDOMを破棄します。DOMを使い終わったら呼び出してください。

メソッド戻り値説明
name()Promise<string | null>ノード名を取得します。
text()Promise<string>テキストを取得します。
attr(name)Promise<string | null>属性値を取得します。
children()Promise<XmlNode[]>子ノードを取得します。
parent()Promise<XmlNode | null>親ノードを取得します。
query(xpath)Promise<XmlNode[]>対象ノード配下をXPathで検索します。

XmlNodeは、検索で見つかった要素やテキストを表します。

よく使うのは、要素内の文字列を取得するtext()と、属性値を取得するattr(name)です。

const links = await dom.query('//a[@href]');
for (const link of links) {
const label = await link.text();
const href = await link.attr('href');
$println(`${label}: ${href ?? ''}`);
}

children()parent()を使うと、XPathではなくツリー構造として前後のノードをたどれます。

const sections = await dom.query('//section');
for (const section of sections) {
const children = await section.children();
$println(`children: ${children.length}`);
}

node.query(xpath)は、そのノード配下を起点に検索します。たとえば、各articleの中にあるタイトルだけを取り出す場合に使えます。

const articles = await dom.query('//article');
for (const article of articles) {
const titles = await article.query('h2');
const title = titles[0] ? await titles[0].text() : '';
$println(title);
}
const dom = await $parser.html($resource.pageHtml);
const links = await dom.query('//a');
for (const link of links) {
const href = await link.attr('href');
const text = await link.text();
$println(`${text}: ${href ?? ''}`);
}
await dom.dispose();

XMLから属性とテキストを抽出する例

Section titled “XMLから属性とテキストを抽出する例”
const xml = `<users>
<user id="001" role="admin">taro</user>
<user id="002" role="member">jiro</user>
</users>`;
const dom = await $parser.xml(xml);
const users = await dom.query('//user[@role="admin"]');
for (const user of users) {
const id = await user.attr('id');
const name = await user.text();
$println(`${id}: ${name}`);
}
await dom.dispose();
  • DomControllerXmlNodeのメソッドは非同期です。基本的にawaitして使います。
  • text()は対象ノード配下のテキストを結合して返します。
  • attr(name)は、属性がない場合にnullを返します。
  • query()で見つからない場合は空配列を返します。
  • 文書全体に対するquery()は、/または//から始めます。
  • ノード配下に対するnode.query()では、h2のように相対的な指定もできます。
  • DOMはRust側に保持されるため、使い終わったらdispose()を呼び出してください。
画像メモ: HTML文字列をresourceに登録し、$parser.html()でタイトルやリンクを抽出してテーブル出力する流れのGIFがあるとよいです。DOMツリーそのものより、「HTMLから必要な情報を取り出す」用途が伝わるものが向いています。