Node.js et le DOM - Atinux

Node.js et le DOM

Dans cet article, nous allons voir comment et pourquoi utiliser le DOM côté serveur via node.js. Cela risque de vous arriver un jour si vous voulez manipuler du HTML mais que vous n’êtes pas dans un navigateur.

Pourquoi l’utiliser ?

Il faut savoir qu’on ne peut pas utiliser de RegEx pour parser du HTML, à moins d’être fou, il y a bien trop de cas à gérer, sans compter que le HTML reçu peut très bien ne pas respecter les standards… Je vous laisse découvrir cette réponse sur StackOverflow concernant les RegEx et le HTML. C’est beaucoup plus simple et pratique d’utiliser le DOM. Combiné à jQuery, c’est juste un gain de temps et de fiabilité énorme. De plus, on peut aussi l’utiliser pour construire un HTML (un peu plus) propre, par exemple, essayez ça dans la console de votre navigateur (n’oubliez pas d’inclure jQuery ou d’aller sur un site utilisant jQuery, comme jquery.com) :

1.
2.
$('<div>').html('<h3>I will be</b><h4>Z').html();
//<h3>I will be</h3><h4>Z</h4>

Je vous laisse imaginer comment faire ça avec les expressions régulières… :-) Il faut savoir qu’il y a 2 possibilités de simuler assez facilement le DOM côté serveur, avec le module node.js jsdom ou en utilisant un headless browser.

Le module jsdom

jsdom est un module node.js entièrement fait en JavaScript et créé par Elijah Insua.

  • Installation
1.
npm install jsdom
  • Utilisation

Il suffit de l’inclure via la méthode require :

1.
var jsdom = require("jsdom");

Je ne vais pas m’attarder sur son API, donc je vous laisse jeter un oeil sur sa documentation disponible sur Github.

  • Exemple

On va récupérer tous les titres des articles provenant de la page d’accueil de ce blog, ils possèdent la classe « entry-title » et se trouvent dans un <a>, cela va donc être le sélecteur jQuery : $(‘.entry-title a’) Nous allons donc avoir un fichier exemple.js :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
var jsdom = require('jsdom');

jsdom.env({
    html: "http://atinux.fr",
    scripts: ["http://code.jquery.com/jquery.js"],
    done: function (errors, window) {
        var $ = window.$;
        var titles = $(".entry-title a");
        titles.each(function () {
            console.log($(this).html());
        });
        window.close();
    }
});

On execute node exemple.js : Résultat jsdom Sympathique et plutôt facile non ?

  • Forces

Une seule dépendance donc qu’un seul require, de bonnes performances, facile à utiliser et le module possède une bonne communauté et est maintenu fréquemment.

  • Faiblesses

C’est une simulation du DOM, donc il y a pas mal de bugs assez inexpliqués et donc un bon nombre d’issues. De plus, essayez cet exemple :

1.
2.
3.
4.
5.
6.
7.
8.
9.
var jsdom = require('jsdom');

jsdom.env({
  html: '<div>',
  scripts: ["http://code.jquery.com/jquery.js"],
  done: function (errors, window) {
    console.log(window.$('<div>').html('<h3>I will be</b><h4>Z').html());
  }
});

Cela donnera « <h3>I will be<h4>Z</h4></h3> », c’est légèrement différent qu’au début. À savoir que dès qu’il s’agit de faire des grosses manipulation avec le DOM, on se retrouve assez rapidement avec des bugs. Ce qui est normal car l’implémentation du DOM est un travail colossal qui demande des années de travail, l’idéal serait d’utiliser un vrai moteur de rendu HTML, comme Webkit (Safari et Chrome), Gecko (Firefox), Trident (IE) ou encore Presto Webkit (Opera). C’est là où intervient le « headless browser » !

Le headless browser PhantomJS

Logo Phantom JS

  • Le head…quoi ?!

Un headless browser est tout simplement un navigateur sans interface graphique. C’est parfait pour vérifier si un site utilise le user agent afin modifier son apparence, pour faire des screenshots complet de sites ou générer son pdf, pour faire des tests coté client, pleins de choses et simuler parfaitement le DOM car ils utilisent un moteur de rendu HTML. PhantomJS implémente WebKit et possède une API en JavaScript, c’est donc parfait pour ceux qui utilisent fréquemment le JavaScript, car ils ne seront pas dépaysé.

  • Installation

Ce qu’il faut savoir, c’est que de base, PhantomJS n’est pas du tout lié à node.js, c’est un programme fait en C++ et développé par Ariya Hidayat, ce n’est donc pas un module node.js et demande un peu plus d’étapes qu’un « npm install XXX » pour son installation. Pour les étapes d’installation, c’est par ici.

  • Utilisation

Regardons rapidement un exemple : test.js

1.
2.
3.
4.
5.
6.
7.
console.log("Chargement du blog d'Atinux...");
var page = require('webpage').create();
var url = 'http://atinux.fr';
page.open(url, function (status) {
    console.log('Status : ' + status);
    phantom.exit();
});

Ensuite il suffit de l’exécuter comme ceci : phantom test.js Ce qui va nous donner : Résultat PhantomJS test Là aussi, je ne vais pas m’approfondir sur le fonctionnement, je vous laisse voir la documentation de PhantomJS.

  • Exemple

Il va nous falloir un fichier qu’on va exécuter avec phantomjs, ce fichier va avoir le même comportement que notre 1er exemple, à savoir récupérer les titres de la page d’accueil du blog. getMyTitles.js

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
var page = require('webpage').create();
page.open('http://atinux.fr', function (status) {
    page.includeJs('http://code.jquery.com/jquery.js', function () {
        var titles = page.evaluate(function() {
            return $('.entry-title a').map(function () {
                return $(this).html();
            }).get();
        });
        console.log(titles.join('\n'));
        phantom.exit();
    });
});

Ok maintenant on teste avec la commande phantomjs getMyTitles.js : Résultat PhantomJS exemple Cependant, ce n’est pas via node.js qu’on vient de le faire, mais heureusement, node.js nous permet d’exécuter un fichier via la fonction execFile, provenant du module « child_process ». On va pouvoir récupérer le résultat via la sortie standard de l’exécution de phantom JS car ce dernier fait des console.log(), ce qui va nous donner : exemple.js

1.
2.
3.
4.
5.
var cp = require('child_process');

cp.execFile('phantomjs', [__dirname + '/getMyTitles.js'], function (err, stdout, stderr) {
        console.log(stdout);
});

Bien entendu, le fichier getMyTitles.js doit se trouver à la même racine que le fichier test.js. Je vous laisse lancer node exemple.js, ça donnera le même résultat.

  • Forces

L’implémentation du DOM via WebKit, c’est sans aucun doute le plus gros avantage que je vois à cette solution pour notre exemple. Il faut savoir aussi que ce projet est vraiment suivi et possède une communauté très active. Pour information, si on créé un fichier transformMyHTML.js :

var page = require('webpage').create();
page.includeJs('http://code.jquery.com/jquery.js', function () {
    var goodHTML = page.evaluate(function() {
        return $('<div>').html('<h3>I will be</b><h4>Z').html();
    });
    console.log(goodHTML)
    phantom.exit();
});

On retrouve bien le même résultat que dans notre navigateur : PhantomJS convert HTML

  • Faiblesses

On doit passer par un environnement externe à node.js, ce qui enlève de la stabilité, surtout si on doit créer un nouveau processus à chaque exécution. Cependant il faut savoir qu’avec un bridge, c’est à dire en ne lançant qu’une seule instance de PhantomJS qui tourne en fond et qui implémente les sockets et qui communique avec l’instance de node.js, on peut améliorer significativement le problème de stabilité. C’est d’ailleurs ce que fait le module node-phantom que je vous conseille grandement d’utiliser, cependant je ne vais pas m’étaler dessus, car l’article commence à être long et je ne veux pas vous perdre :)

Donc ?

Comme vous pouvez le constater, chaque solution proposée possède des forces et des faiblesses, de mon point de vue, les deux sont pertinentes et votre choix va dépendre de votre utilisation du DOM coté serveur.

N’hésitez pas à me dire dans les commentaires quelle solution vous utilisez et pour quels besoins :)

Photo par Prachit Punyapor.

2 réflexions au sujet de « Node.js et le DOM »

    1. Sébastien Chopin Auteur de l’article

      Merci, c’est corrigé :)
      J’ai d’ailleurs précisé que leur moteur de rendu est dorénavant Webkit.

      PS : franchement pas mal le nouveau design de ton site !

      Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>