用JS解析HTML字符串

sta*_*age 227 html javascript dom html-parsing

我搜索了一个解决方案,但没有任何相关性,所以这是我的问题:

我想解析一个包含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);
Run Code Online (Sandbox Code Playgroud)

我的目标是从HTML外部页面中提取链接,就像字符串一样.

你知道一个API来做吗?

Flo*_*ine 347

创建一个虚拟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
Run Code Online (Sandbox Code Playgroud)

编辑:添加一个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
Run Code Online (Sandbox Code Playgroud)

  • 请注意:使用此解决方案,如果我执行"alert(el.innerHTML)",则会丢失<html>,<body>和<head>标记.... (8认同)
  • @stage我参加聚会有点晚了,但你应该能够使用`document.createElement('html');`来保存`<head>`和`<body>`标签. (3认同)
  • 我担心将其作为最佳答案。下面的[parse()`](/sf/ask/740952061/#55046067)解决方案更可重用且更优雅。 (3认同)
  • 这不是一个理想的解决方案,因为如果 html 字符串包含图像,浏览器将尝试获取它们!因此,这是我们可能不希望出现的解析副作用:请参阅此示例: var html = "&lt;div&gt;&lt;img src=\"img_girl.jpg\" width=\"500\" height=\"600\ "&gt;&lt;/div&gt;"; var div = document.createElement('div'); div.innerHTML = html; (3认同)
  • 问题:我需要从 &lt;frame&gt; 标签获取链接。但是使用此解决方案,框架标记被删除... (2认同)
  • 它看起来像是在html元素中放置一个html元素 (2认同)
  • 安全说明:这将执行输入中的任何脚本,因此不适合不受信任的输入。 (2认同)

Cil*_*lan 201

这很简单:

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

根据MDN,要在chrome中执行此操作,您需要解析为XML,如下所示:

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

它目前不受webkit的支持,您必须遵循Florian的答案,并且在大多数情况下在移动浏览器上工作是未知的.

编辑:现在广泛支持

  • 值得注意的是,2016年DOMParser现已得到广泛支持.http://caniuse.com/#feat=xml-serializer (33认同)
  • "text/html"在Chrome上正常运行 (7认同)
  • 值得注意的是,创建文档中的所有相对链接都被破坏了,因为文档是通过继承`window`的`documentURL`来创建的,这很可能与字符串的URL不同. (4认同)
  • 值得一提的是,您应该*仅*一次调用`new DOMParser`,然后在脚本的其余部分中重用同一对象。 (2认同)

Mun*_*war 16

编辑:下面的解决方案仅适用于HTML"片段",因为删除了html,head和body.我想这个问题的解决方案是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>');
Run Code Online (Sandbox Code Playgroud)

对于旧版浏览器,我已经将jQuery的parseHTML()方法提取到一个独立的要点 - https://gist.github.com/Munawwar/6e6362dbdf77c7865a99


Mat*_*ieu 8

var $doc = new DOMParser().parseFromString($html, "text/html");
$As = $('a', $doc);
Run Code Online (Sandbox Code Playgroud)

  • 你为什么用前缀'$`?另外,如[链接重复](http://stackoverflow.com/questions/9250545/javascript-domparser-access-innerhtml-and-other-properties)中所述,`text/html`不受支持,并且必须使用polyfill实现. (4认同)
  • 我从项目中复制了这一行,我习惯在 javascript 应用程序(不在库中)中使用 $ 为变量添加前缀。只是为了避免与图书馆发生冲突。这不是很有用,因为几乎每个变量都有作用域,但它曾经很有用。它还(也许)有助于轻松识别变量。 (2认同)

Joh*_*ers 6

以下函数parseHTML将返回:


代码 :

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

如何使用 :

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

  • @Toothbrush:到2017年初,IE8支持仍然有意义吗? (2认同)

小智 6

我认为最好的方法是像这样使用这个API:

//Table string in HTML format
const htmlString = '<table><tbody><tr><td>Cell 1</td><td>Cell 2</td></tr></tbody></table>';

//Parse using DOMParser native way
const parser = new DOMParser();
const $newTable = parser.parseFromString(htmlString, 'text/html');

//Here you can select parts of your parsed html and work with it
const $row = $newTable.querySelector('table > tbody > tr');

//Here i'm printing the number of columns (2)
const $containerHtml = document.getElementById('containerHtml');
$containerHtml.innerHTML = ['Your parsed table have ', $row.cells.length, 'columns.'].join(' ');
Run Code Online (Sandbox Code Playgroud)
<div id="containerHtml"></div>
Run Code Online (Sandbox Code Playgroud)


Joe*_*ard 5

在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;
Run Code Online (Sandbox Code Playgroud)

我建议创建一个辅助函数,如果可用则使用createContextualFragment,否则返回innerHTML.

基准:http://jsperf.com/domparser-vs-createelement-innerhtml/3

  • 安全说明:这将执行输入中的任何脚本,因此不适合不受信任的输入。 (2认同)

Ant*_*ris 5

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>`) );
Run Code Online (Sandbox Code Playgroud)


只有Node父级Node( 的开头)中的有效 childRange才会被解析。否则,可能会出现意想不到的结果:

// <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>');
Run Code Online (Sandbox Code Playgroud)

  • 安全说明:这将执行输入中的任何脚本,因此不适合不受信任的输入。 (4认同)

*_*лов 5

1 种方式

使用document.cloneNode()

性能为:

调用document.cloneNode()大约花费了 0.22499999977299012 毫秒。

也许还会更多。

var t0, t1, html;

t0 = performance.now();
   html = document.cloneNode(true);
t1 = performance.now();

console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")

html.documentElement.innerHTML = '<!DOCTYPE html><html><head><title>Test</title></head><body><div id="test1">test1</div></body></html>';

console.log(html.getElementById("test1"));
Run Code Online (Sandbox Code Playgroud)

2种方法

使用document.implementation.createHTMLDocument()

性能为:

调用document.implementation.createHTMLDocument()大约花费了 0.14000000010128133 毫秒。

var t0, t1, html;

t0 = performance.now();
html = document.implementation.createHTMLDocument("test");
t1 = performance.now();

console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")

html.documentElement.innerHTML = '<!DOCTYPE html><html><head><title>Test</title></head><body><div id="test1">test1</div></body></html>';

console.log(html.getElementById("test1"));
Run Code Online (Sandbox Code Playgroud)

3路

使用document.implementation.createDocument()

性能为:

调用document.implementation.createHTMLDocument()大约花费了 0.14000000010128133 毫秒。

var t0 = performance.now();
  html = document.implementation.createDocument('', 'html', 
             document.implementation.createDocumentType('html', '', '')
         );
var t1 = performance.now();

console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")

html.documentElement.innerHTML = '<html><head><title>Test</title></head><body><div id="test1">test</div></body></html>';

console.log(html.getElementById("test1"));
Run Code Online (Sandbox Code Playgroud)

4路

使用new Document()

性能为:

调用document.implementation.createHTMLDocument()大约花费了 0.13499999840860255 毫秒。

  • 笔记

ParentNode.append是2020年的实验技术。

var t0 = performance.now();
  html = document.implementation.createDocument('', 'html', 
             document.implementation.createDocumentType('html', '', '')
         );
var t1 = performance.now();

console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")

html.documentElement.innerHTML = '<html><head><title>Test</title></head><body><div id="test1">test</div></body></html>';

console.log(html.getElementById("test1"));
Run Code Online (Sandbox Code Playgroud)


Dan*_*lan 5

要在 node.js 中执行此操作,您可以使用 HTML 解析器,例如node-html-parser。语法如下:

import { parse } from 'node-html-parser';

const root = parse('<ul id="list"><li>Hello World</li></ul>');

console.log(root.firstChild.structure);
// ul#list
//   li
//     #text

console.log(root.querySelector('#list'));
// { tagName: 'ul',
//   rawAttrs: 'id="list"',
//   childNodes:
//    [ { tagName: 'li',
//        rawAttrs: '',
//        childNodes: [Object],
//        classNames: [] } ],
//   id: 'list',
//   classNames: [] }
console.log(root.toString());
// <ul id="list"><li>Hello World</li></ul>
root.set_content('<li>Hello World</li>');
root.toString();    // <li>Hello World</li>
Run Code Online (Sandbox Code Playgroud)