JSでHTML文字列を解析する


258

私は解決策を探しましたが、何も関連性がなかったので、ここに私の問題があります:

HTMLテキストを含む文字列を解析したい。JavaScriptでやりたいです。

このライブラリを試しましたが、文字列からではなく、現在のページのHTMLを解析しているようです。下のコードを試すと、ページのタイトルが変わるからです。

var parser = new HTMLtoDOM("<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>", document);

私の目標は、文字列のように読んだHTML外部ページからリンクを抽出することです。

それを行うためのAPIを知っていますか?



1
リンクされた複製のメソッドは、指定された文字列からHTMLドキュメントを作成します。次に、を使用doc.getElementsByTagName('a')してリンク(またはdoc.links)を読み取ることができます。
Rob W

それの価値はあなたがReact.jsのようなフレームワークを使用している場合、その後のようなフレームワークに固有のもので、それを行う方法があるかもしれないことに言及:stackoverflow.com/questions/23616226/...
マイク・ライアン

これはあなたの質問に答えますか?テキストJavaScriptからHTMLを
取り除く

回答:


373

ダミーのDOM要素を作成し、それに文字列を追加します。その後、他のDOM要素と同じように操作できます。

var el = document.createElement( 'html' );
el.innerHTML = "<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>";

el.getElementsByTagName( 'a' ); // Live NodeList of your anchor elements

編集:ファンを喜ばせるjQuery回答を追加!

var el = $( '<div></div>' );
el.html("<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>");

$('a', el) // All the anchor elements

9
ちょうどノート:私は「警告(el.innerHTML)」を行う場合は、このソリューションを使用すると、私は<HTML>、<BODY>と<ヘッド>タグを失う....
段階

2
問題:<frame>タグからリンクを取得する必要があります。しかし、このソリューションでは、フレームタグが削除されます...
ステージ

3
@stageパーティーに少し遅れますがdocument.createElement('html');<head><body>タグを保持するために使用できるはずです。
omn​​inonsense

3
html要素内にhtml要素を配置しているようです
symbiont

6
一番の答えは賛成投票だと思います。parse()以下の解決策は、再利用可能でエレガントです。
ジャスティン

233

それは非常に簡単です:

var parser = new DOMParser();
var htmlDoc = parser.parseFromString(txt, 'text/html');
// do whatever you want with htmlDoc.getElementsByTagName('a');

MDNよるとこれをChromeで行うには、次のようにXMLとして解析する必要があります。

var parser = new DOMParser();
var htmlDoc = parser.parseFromString(txt, 'text/xml');
// do whatever you want with htmlDoc.getElementsByTagName('a');

それは現在webkitではサポートされておらず、Florianの回答に従う必要があります。ほとんどの場合、モバイルブラウザーで動作するかどうかは不明です。

編集:広くサポートされるようになりました


35
2016年にDOMParserが広くサポートされるようになったことは注目に値します。caniuse.com/#feat=xml-serializer
aendrew

5
文書が継承することによって作成されるので、作成したドキュメント内のすべての相対リンクは、壊れていることは注目に値するdocumentURLwindow文字列のURLから、これは最も可能性の高い異なります。
ceving

2
一度だけ呼び出してnew DOMParserから、スクリプトの残りの部分で同じオブジェクトを再利用する必要があることに注意してください。
ジャックギ

1
parse()以下の解決策は、再利用性が高く、HTMLに固有のものです。ただし、XMLドキュメントが必要な場合は、これは便利です。
ジャスティン

この解析されたWebページをダイアログボックスなどに表示するにはどうすればよいですか?私はそれに対する解決策を見つけることができませんでした
Shariq Musharaf

18

編集:html、head、bodyが削除されているため、以下の解決策はHTMLの「フラグメント」のみを対象としています。この質問の解決策は、DOMParserのparseFromString()メソッドだと思います。


HTMLフラグメントの場合、ここにリストされているソリューションはほとんどのHTMLで機能しますが、特定のケースでは機能しません。

たとえば、解析を試してください<td>Test</td>。これは、div.innerHTMLソリューション、DOMParser.prototype.parseFromString、range.createContextualFragmentソリューションでは機能しません。tdタグがなくなり、テキストのみが残ります。

jQueryだけがそのケースをうまく処理します。

したがって、将来のソリューション(MS Edge 13+)はテンプレートタグを使用することです。

function parseHTML(html) {
    var t = document.createElement('template');
    t.innerHTML = html;
    return t.content.cloneNode(true);
}

var documentFragment = parseHTML('<td>Test</td>');

古いブラウザのために私は独立した要旨にjQueryのparseHTML()メソッドを抽出している- https://gist.github.com/Munawwar/6e6362dbdf77c7865a99


古いブラウザでも動作する上位互換のコードを記述したい場合は<template>タグポリフィルできます。これは、ポリフィルする必要があるカスタム要素に依存します。実際、webcomponents.js を使用して、カスタム要素、テンプレート、シャドウDOM、プロミスなどのいくつかの要素を一度にポリフィルすることができます。
ジェフラフリン2017

12
var doc = new DOMParser().parseFromString(html, "text/html");
var links = doc.querySelectorAll("a");

4
なぜプレフィックスを付けるの$ですか?また、リンクされた複製で述べたように、text/htmlはあまりサポートされておらず、ポリフィルを使用して実装する必要があります。
Rob W

1
この行をプロジェクトからコピーしました。JavaScriptアプリケーション(変数ではない)では、変数の前に$を付けるのに慣れています。図書館との衝突を避けるためです。ほとんどすべての変数にスコープが設定されているため、それほど有用ではありませんが、以前は有用でした。また、(おそらく)変数を簡単に特定するのにも役立ちます。
Mathieu

1
残念ながらDOMParserどちらの作業text/htmlChromeで、このMDNのページには、回避策を提供します。
ジョークスター2013

セキュリティ上の注意:これはブラウザーコンテキストなしで実行されるため、スクリプトは実行されません。信頼できない入力に適しているはずです。
Leif Arne Storset

6

ChromeとFirefoxでHTMLを解析する最も速い方法は、Range#createContextualFragmentです。

var range = document.createRange();
range.selectNode(document.body); // required in Safari
var fragment = range.createContextualFragment('<h1>html...</h1>');
var firstNode = fragment.firstChild;

可能な場合はcreateContextualFragmentを使用し、それ以外の場合はinnerHTMLにフォールバックするヘルパー関数を作成することをお勧めします。

ベンチマーク:http : //jsperf.com/domparser-vs-createelement-innerhtml/3


(シンプル)のように、という注意innerHTML、これが実行される<img>のをonerror
Ry-

これの問題は、「<td> test </ td>」のようなhtmlがdocument.bodyコンテキストのtdを無視することです(そして、「test」テキストノードのみを作成します)。OTOH(テンプレートエンジンで内部的に使用されている場合)その後、適切なコンテキストが利用可能になります。
Munawwar、2015年

また、IE 11はcreateContextualFragmentをサポートしています。
Munawwar、2015年

問題は、JSで解析する方法
でした-Chrome

セキュリティ上の注意:これは入力内のすべてのスクリプトを実行するため、信頼できない入力には適していません。
Leif Arne Storset

6

次の関数parseHTMLはどちらかを返します。

  • a Documentファイルがdoctypeで始まる場合。

  • a DocumentFragmentファイルがdoctypeで始まっていない場合。


コード :

function parseHTML(markup) {
    if (markup.toLowerCase().trim().indexOf('<!doctype') === 0) {
        var doc = document.implementation.createHTMLDocument("");
        doc.documentElement.innerHTML = markup;
        return doc;
    } else if ('content' in document.createElement('template')) {
       // Template tag exists!
       var el = document.createElement('template');
       el.innerHTML = markup;
       return el.content;
    } else {
       // Template tag doesn't exist!
       var docfrag = document.createDocumentFragment();
       var el = document.createElement('body');
       el.innerHTML = markup;
       for (i = 0; 0 < el.childNodes.length;) {
           docfrag.appendChild(el.childNodes[i]);
       }
       return docfrag;
    }
}

使い方 :

var links = parseHTML('<!doctype html><html><head></head><body><a>Link 1</a><a>Link 2</a></body></html>').getElementsByTagName('a');

これをIE8で動作させることができませんでした。関数の最初の行で「オブジェクトはこのプロパティまたはメソッドをサポートしていません」というエラーが表示されます。createHTMLDocument関数は存在しないと思います
Sebastian Carroll

あなたのユースケースは正確には何ですか?HTMLを解析するだけで、HTMLがドキュメントの本文を対象としている場合は、次のようにできます。(1)var div = document.createElement( "DIV"); (2)div.innerHTML =マークアップ; (3)結果= div.childNodes; ---これは子ノードのコレクションを提供し、IE8だけでなくIE6-7でも機能するはずです。
John Slegers 2014年

別のオプションをありがとう、これをもう一度行う必要がある場合は試してみる。今のところ、私は上記のJQueryソリューションを使用しました。
セバスチャンキャロル

@SebastianCarroll IE8はtrim文字列のメソッドをサポートしていないことに注意してください。stackoverflow.com/q/2308134/3210837を参照してください。
歯ブラシ2016

2
@Toothbrush:2017年の明け方にIE8のサポートはまだ関連していますか?
John Slegers 2016

4

jQueryを使用することにオープンであれば、HTMLの文字列から分離されたDOM要素を作成するための便利な機能がいくつかあります。これらは通常の方法で照会できます。例:

var html = "<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>";
var anchors = $('<div/>').append(html).find('a').get();

編集-ちょうど正しい@Florianの答えを見ました。これは基本的には彼の言ったこととまったく同じですが、jQueryを使用しています。


4
const parse = Range.prototype.createContextualFragment.bind(document.createRange());

document.body.appendChild( parse('<p><strong>Today is:</strong></p>') ),
document.body.appendChild( parse(`<p style="background: #eee">${new Date()}</p>`) );


NodeNode(の先頭Range)内の 有効な子のみが解析されます。そうしないと、予期しない結果が発生する可能性があります。

// <body> is "parent" Node, start of Range
const parseRange = document.createRange();
const parse = Range.prototype.createContextualFragment.bind(parseRange);

// Returns Text "1 2" because td, tr, tbody are not valid children of <body>
parse('<td>1</td> <td>2</td>');
parse('<tr><td>1</td> <td>2</td></tr>');
parse('<tbody><tr><td>1</td> <td>2</td></tr></tbody>');

// Returns <table>, which is a valid child of <body>
parse('<table> <td>1</td> <td>2</td> </table>');
parse('<table> <tr> <td>1</td> <td>2</td> </tr> </table>');
parse('<table> <tbody> <td>1</td> <td>2</td> </tbody> </table>');

// <tr> is parent Node, start of Range
parseRange.setStart(document.createElement('tr'), 0);

// Returns [<td>, <td>] element array
parse('<td>1</td> <td>2</td>');
parse('<tr> <td>1</td> <td>2</td> </tr>');
parse('<tbody> <td>1</td> <td>2</td> </tbody>');
parse('<table> <td>1</td> <td>2</td> </table>');

セキュリティ上の注意:これは入力内のすべてのスクリプトを実行するため、信頼できない入力には適していません。
Leif Arne Storset

0

この簡単なコードでそれを行うことができます:

let el = $('<div></div>');
$(document.body).append(el);
el.html(`<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>`);
console.log(el.find('a[href="test0"]'));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.