結構政府関連のAPIとか、古い型であるXMLデータであることが多い。
XMLデータもリクエスト送ってパースしてweb上に描画、とかできた方がいいよね、と思って勉強。
どうも今まで使ってたfetch
が上手く使えないっぽい。ええ...
##XPath?
XPath
を使うと取得しやすいとか。なにそれ!
で、調べてみたけど全然データがない。Qiitaも殆ど記事がない。
人気ないのかなあ。で、色々この辺のサイトを見つけました。
https://uhyohyo.net/javascript/10_1.html
https://www.techscore.com/tech/XML/XPath/XPath1/xpath01.html/
http://cloudcafe.tech/?p=2456
ちょっと脇道に逸れるけどXPathについてきちんと学んでみます。
XPath・・・XMLデータから特定の部分を抽出するための簡潔な言語
CSSではセレクタを使ってHTML文書内の特定の部分を抽出しますが、XPathはより簡潔かつ柔軟に指定ができるとされています。
おー。なんか良さそうですね。なんであんまり情報がないんでしょうか。(不安)
##XPathの文法・軸編
/html/body/p
パス指定と似ていますね。htmlの子ノードのbodyの子ノードのp、を表しています。
この/要素名
は、
軸::ノードテスト
の略であり、これはロケーションステップと呼ばれています。
軸の初期値はchild
なので、
/p
= /child::p
ということ。
軸は色々ある。
軸 名 | 軸の説明 |
---|---|
child | 直接の子のみ |
descendant | 子孫ノード。子全て |
ancestor | 親ノード全て |
parent | 直接の親のみ |
precending-sibling | 兄弟ノードのうち自分より前のノード |
following-sibling | 兄弟ノードのうち自分より後のノード |
self | 自分自身のみ |
descendant-or-self | 子孫と自分自身 |
ancestor-self | 自分より上と自分自身 |
https://www.xmldb.jp/tech/xpath_1.php | |
ここにリストがあった。 |
##XPathの文法・ノードテスト編
*
・・・要素ノード全て。(テキストノード等は含まない)
note()
・・・全てのテキストノード。
comment()
・・・コメントノード。
node()
・・・全ての種類のノード。
ちなみによく使われるself::node()
というロケーションステップは、
.
で省略可能です。
self軸の全てのノードって当然自分自身ですよね。何に使うんだ、、本当によく使われるのか?
/html/body/././pのように、間に挟んだりしても関係ありません。
https://uhyohyo.net/javascript/10_1.html
parent::node()
は..
で省略可能です。
直接の親の全て、、なのでこれも当然1つです。
descendant-or-self::node()
は//
で省略可能です。
自分自身とその子孫全てですね。これは使えそうです。
##以上を踏まえて、DOMで使ってみる!
document.evcaluateメソッド
・・・XPath 式やその他の引数に基づき XPathResult を返す
やっとこさJavaScript上でXPathを使えそうです。
var headings = document.evaluate("/html/body//h2", document, null, XPathResult.ANY_TYPE, null);
第一引数・・・XPath文
第二引数・・・ルートノード。このノードから下で検索する。
第三引数・・・名前空間解決器(?)
第四引数・・・結果タイプの指定。
第五引数・・・再利用するオブジェクト。
名前空間解決器(?)
XMLやXHTMLでは名前空間がついてることがあるらしい。
<zhtml:html>
←こんなの。
名前空間接頭辞をつけたのは属性で設定しているらしい。
ここについては必要になった時調べるぐらいで良さそうなので勉強省略。
結果タイプ
XPathResultのプロパティを指定。
https://developer.mozilla.org/ja/docs/Web/API/Document/evaluate
頑張って意訳してみましょう。。
結果タイプ | 値 | 説明 |
---|---|---|
ANY_TYPE | 0 | 与えられた表記のどんなタイプでも全部 |
NUMBER_TYPE | 1 | 数値を含む結果を1つセットする(count()ファンクションのとき便利) |
STRING_TYPE | 2 | 文字列を含む結果を1つセットする |
BOOLEAN_TYPE | 3 | ブーリアン値を含む結果を1つセットする(not()ファンクションのとき便利) |
UNORDERED_NODE_ITERATOR_TYPE | 4 | 表現にあうノード全てを結果にセットする。順はぐちゃぐちゃ |
ORDERED_NODE_ITERATOR_TYPE | 5 | 表現にあうノード全てを結果にセットする。順は文書内通り |
UNORDERED_NODE_SNAPSHOT_TYPE | 6 | 表現にあうノードをスナップショットで結果にセットする。順はぐちゃぐちゃ |
ORDERED_NODE_SNAPSHOT_TYPE | 7 | 表現にあうノードをスナップショットで結果にセットする。順は文書内通り |
ANY_UNORDERED_NODE_TYPE | 8 | 表現にマッチしてた1つのノードを返す。ただし最初のやつとは限らない |
FIRST_ORDERED_NODE_TYPE | 9 | 表現にマッチしてた最初のノードを返す。 |
複数のノードをキャッチできるものは、取得したXPathResultオブジェクトからそれを扱うためのプロパティがあります。
・スナップショット系の場合
snapshotLength
・・・結果のノード数
snapshotItem
・・・ノードへのアクセス
スナップショット系は、evaluateメソッド実行時に全ての探索をしてしまいます。
let result = document.evaluate("/html/body//h2", document, null, XPathResult.ANY_TYPE, null);
for(var i=0; i<result.snapshotLength; i++){
console.log(result.snapshotItem(i));
}
UNORDERED_NODE_ITERATOR_TYPE, ORDERED_NODE_ITERATOR_TYPE
この2つに関しては、次のノードが必要なたびに探索をする、という仕組みになっているので注意です。
なのでアクセス時引数がありません!
iterateNextメソッドを使います。
var node;
while(node = result.iterateNext()){
console.log(node);
}
ITERATOR系はその仕様から、木構造に変化があったら、
のちにノードの操作をしようとしてもエラーになります。
##document.evaluateが人気ない(?)謎って...
https://caniuse.com/#search=evaluate
IEが全然対応してないみたいですね...ぐむ