用 JS 解析 HTML 字符串

问题描述 投票:0回答:16

我想解析一个包含 HTML 文本的字符串。我想用 JavaScript 来做。

我尝试了纯 JavaScript HTML 解析器库,但它似乎解析我当前页面的 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 可以做到这一点吗?

javascript html dom html-parsing
16个回答
500
投票

创建一个虚拟 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

410
投票

很简单:

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

根据 MDN,要在 chrome 中执行此操作,您需要像这样解析为 XML:

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

目前 webkit 不支持它,你必须遵循 Florian 的回答,并且在大多数情况下在移动浏览器上工作尚不清楚。

编辑:现在得到广泛支持


40
投票

编辑:下面的解决方案仅适用于 HTML“片段”,因为 html、head 和 body 被删除。我猜这个问题的解决方案是 DOMParser 的 parseFromString() 方法:

const parser = new DOMParser();
const document = parser.parseFromString(html, "text/html");

对于 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;
}

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

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


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

8
投票
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>`) );


仅解析父级
Node
Node
的开头)内的有效子级
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>');

7
投票

在 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


7
投票

以下函数

parseHTML
将返回:

  • a

    Document
    当您的文件以文档类型开头时。

  • a

    DocumentFragment
    当您的文件不以文档类型开头时。


代码:

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');

6
投票

我认为最好的方法是使用this这样的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(' ');
<div id="containerHtml"></div>


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"));

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"));

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"));

4 路

使用

new Document()

性能为:

调用

document.implementation.createHTMLDocument()
花费了 ~0.13499999840860255 毫秒。

  • 注意

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

var t0, t1, html;

t0 = performance.now();
//---------------
html = new Document();

html.append(
  html.implementation.createDocumentType('html', '', '')
);
    
html.append(
  html.createElement('html')
);
//---------------
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">test1</div></body></html>';

console.log(html.getElementById("test1"));

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>

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。


1
投票
const html =
`<script>
    alert('👋 there ! Wanna grab a 🍺'); 
</script>`;

const scriptEl = document.createRange().createContextualFragment(html);
parent.append(scriptEl);

我找到了这个解决方案,我认为这是最好的解决方案,它解析 HTML 并执行里面的脚本。


0
投票

我必须使用在 Angular NGX Bootstrap 弹出窗口的弹出窗口中解析的元素的innerHTML。这是对我有用的解决方案。

  public htmlContainer = document.createElement( 'html' );

在构造函数中

this.htmlContainer.innerHTML = ''; setTimeout(() => { this.convertToArray(); }); 

 convertToArray() {
    const shapesHC = document.getElementsByClassName('weekPopUpDummy');
    const shapesArrHCSpread = [...(shapesHC as any)];
    this.htmlContainer = shapesArrHCSpread[0];
    this.htmlContainer.innerHTML = shapesArrHCSpread[0].textContent;
  }

在 html 中

<div class="weekPopUpDummy" [popover]="htmlContainer.innerHTML" [adaptivePosition]="false" placement="top" [outsideClick]="true" #popOverHide="bs-popover" [delay]="150" (onHidden)="onHidden(weekEvent)" (onShown)="onShown()">

0
投票
function parseElement(raw){
    let el = document.createElement('div');
    el.innerHTML = raw;
    let res = el.querySelector('*');
    res.remove();
    return res;
}

注意:原始字符串不应超过 1 个元素


0
投票

我使用了我从这个博客中提到的 DOMParser 类。

这将返回一个 HTMLCollection 对象,我们可以在其中将元素作为 DOM 元素本身进行访问。也可以轻松地在 HTML 文档中插入代码

document.body.append(...parseHTML(html_string));

const parseHTML = (htmlString) => {
    const parser = new DOMParser();
    const page = parser.parseFromString(htmlString, 'text/html');
    return page.body.children;
};

稍后谢谢我。


-1
投票
let content = "<center><h1>404 Not Found</h1></center>"
let result = $("<div/>").html(content).text()

内容:

<center><h1>404 Not Found</h1></center>
,
结果:
"404 Not Found"

© www.soinside.com 2019 - 2024. All rights reserved.