363 votes

Analyser une chaîne HTML avec JS

Je veux analyser une chaîne qui contient du texte HTML. Je veux le faire en JavaScript.

J'ai essayé le Bibliothèque d'analyseur HTML en JavaScript pur mais il semble qu'il analyse le HTML de ma page actuelle, et non une chaîne de caractères. Car lorsque j'essaie le code ci-dessous, le titre de ma page est modifié :

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

Mon objectif est d'extraire des liens d'une page HTML externe que je lis comme une chaîne de caractères.

Connaissez-vous une API pour le faire ?

7voto

Joel Points 1087

La méthode la plus rapide pour analyser le HTML dans Chrome et Firefox est Range#createContextualFragment :

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

Je recommande de créer une fonction d'aide qui utilise createContextualFragment si elle est disponible et revient à innerHTML dans le cas contraire.

Point de repère : http://jsperf.com/domparser-vs-createelement-innerhtml/3

6voto

AnthumChris Points 1778
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>`) );

Seulement enfant valide Node au sein du parent Node (début de la Range ) seront analysés. Sinon, des résultats inattendus peuvent se produire :

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

4voto

jmar777 Points 11681

Si vous avez l'intention d'utiliser jQuery, il offre de belles possibilités pour créer des éléments DOM détachés à partir de chaînes de caractères HTML. Ceux-ci peuvent ensuite être interrogés par les moyens habituels, par exemple :

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

Edit - je viens de voir la réponse de @Florian qui est correcte. C'est en fait exactement ce qu'il a dit, mais avec jQuery.

4voto

1 voie

Utilisez document.cloneNode()

La performance est :

Appel à document.cloneNode() a pris ~0.22499999977299012 millisecondes.

et il y en aura peut-être d'autres.

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 voies

Utilisez document.implementation.createHTMLDocument()

La performance est :

Appel à document.implementation.createHTMLDocument() a pris ~0.14000000010128133 millisecondes.

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 voies

Utilisez document.implementation.createDocument()

La performance est :

Appel à document.implementation.createHTMLDocument() a pris ~0.14000000010128133 millisecondes.

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 voies

Utilisez new Document()

La performance est :

Appel à document.implementation.createHTMLDocument() a pris ~0.13499999840860255 millisecondes.

  • Note

ParentNode.append est une technologie expérimentale pour l'année 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"));

4voto

tieTYT Points 15326

Pour faire cela dans node.js, vous pouvez utiliser un analyseur HTML tel que node-html-parser . La syntaxe ressemble à ceci :

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>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X