4 outils pour faire du Web Scraping en Node.js

April 29, 2020
Rédigé par
Sam Agnew
Twilion
Révisé par

4-outils-web-scraping-node-js

Les données dont vous avez besoin sont parfois disponibles en ligne, mais ne sont pas disponibles via une API REST dédiée.

Heureusement pour les développeurs JavaScript, il existe une variété d'outils disponibles dans Node.js pour le scraping et l'analyse des données directement à partir de sites web à exploiter dans vos projets et applications.

Parcourons quatre de ces bibliothèques pour voir comment elles fonctionnent et comment elles se comparent les unes aux autres !

Mais d’abord, assurez-vous que vous avez des versions à jour de Node.js (au moins 12.0.0) et de npm installées sur votre machine. Exécutez la commande suivante dans le répertoire où vous souhaitez placer votre code:

npm init --yes

Pour certaines de ces applications, nous utiliserons la bibliothèque Got pour faire des requêtes HTTP. Installez-la dans le même répertoire, avec cette commande:

npm install got@11.0.2

Essayons maintenant de trouver tous les liens vers des fichiers MIDI uniques sur la page web des Archives de Musique de Jeux Vidéos. Nous allons prendre un bon paquet de sons Nintendo comme exemple de problème à résoudre pour chacune de ces bibliothèques,

capture d'cran d'une liste de musiques de jeu vidéo

Conseils et astuces pour le web scraping

Nous passerons bientôt à des outils plus spécifiques, mais il existe aussi quelques thèmes communs qui seront utiles - quelle que soit la méthode que vous décidez d'utiliser.

Avant d'écrire du code pour analyser le contenu que vous voulez, vous devez généralement jeter un coup d'œil au HTML rendu par le navigateur. Chaque page web est différente et, parfois, l'extraction des bonnes données nécessite un peu de créativité, de reconnaissance des formes et d'expérimentation.

La plupart des navigateurs modernes mettent à votre disposition des outils de développement utiles. Si vous cliquez avec le bouton droit de la souris sur l'élément qui vous intéresse, vous pouvez inspecter le code HTML qui se cache derrière cet élément pour en savoir plus.

inspection des éléments dans le navigateur

Souvent, vous aurez aussi besoin de filtrer un contenu spécifique. Pour ce faire, on utilise souvent des sélecteurs CSS (que vous verrez dans les exemples de code de ce tutoriel) pour rassembler les éléments HTML qui répondent à un critère spécifique. Les expressions régulières sont également très utiles dans de nombreuses situations de web scraping. En outre, si vous avez besoin d'un peu plus de granularité, vous pouvez écrire des fonctions pour filtrer le contenu des éléments, comme celle ci-dessous, afin de déterminer si une balise de lien hypertexte fait référence à un fichier MIDI:

const isMidi = (link) => {
  // renvoie false si l'attribut href n'est pas présent
  if(typeof link.href === 'undefined') { return false }
 
  return link.href.includes('.mid');
};

Nota-bene : De nombreux sites interdisent le web scraping dans leurs conditions d'utilisation, alors pensez toujours à vérifier ce point préalablement.

Sur ce, entrons dans le vif du sujet !

jsdom

jsdom est une implémentation de nombreux standards web pour Node.js en pure-JavaScript. C’est un excellent outil pour tester et récupérer des applications web. Installez-le dans votre terminal en utilisant la commande suivante :

npm install jsdom@16.4.0

Vous n’avez besoin que du code suivant pour rassembler tous les liens vers les fichiers MIDI sur la page Video Game Music Archive référencée plus haut:

const got = require('got');
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
 
const vgmUrl= 'https://www.vgmusic.com/music/console/nintendo/nes';
 
const isMidi = (link) => {
  // renvoie false si l'attribut href n'est pas présent
  if(typeof link.href === 'undefined') { return false }
 
  return link.href.includes('.mid');
};
 
const noParens = (link) => {
  // Expression Régulière qui détermine si le texte comporte des parenthèses.
  const parensRegex = /^((?!\().)*$/;
  return parensRegex.test(link.textContent);
};
 
(async () => {
  const response = await got(vgmUrl);
  const dom = new JSDOM(response.body);
 
  // Créée un tableau à partir des éléments HTML pour les filtrer
  const nodeList = [...dom.window.document.querySelectorAll('a')];
 
  nodeList.filter(isMidi).filter(noParens).forEach(link => {
    console.log(link.href);
  });
})();

Ce code utilise le sélecteur de requête très simple a pour accéder à tous les hyperliens de la page, ainsi que quelques fonctions pour filtrer ce contenu et s'assurer que nous ne recevons que les fichiers MIDI que nous voulons.

La fonction de filtrage noParens() utilise une expression régulière pour exclure tous les fichiers MIDI qui contiennent des parenthèses : il n’y aura pas de versions alternatives de la même chanson.

Enregistrez ce code dans un fichier nommé index.js, et exécutez-le avec la commande node index.js dans votre terminal.

Si vous souhaitez une présentation plus approfondie de cette bibliothèque, consultez l'autre tutoriel que j'ai rédigé sur l'utilisation de jsdom.

Cheerio

Cheerio est une bibliothèque similaire à jsdom, mais elle est conçue pour être plus légère, ce qui la rend beaucoup plus rapide. Elle met en œuvre un sous-ensemble du noyau de jQuery, fournissant une API avec laquelle de nombreux développeurs JavaScript sont familiers.

Installez-la avec la commande suivante:

npm install cheerio@1.0.0-rc.3

Le code dont nous avons besoin pour accomplir cette même tâche est très similaire:

const cheerio = require('cheerio');
const got = require('got');
 
const vgmUrl= 'https://www.vgmusic.com/music/console/nintendo/nes';
 
const isMidi = (i, link) => {
  // renvoie false si l'attribut href n'est pas présent
  if(typeof link.attribs.href === 'undefined') { return false }
 
  return link.attribs.href.includes('.mid');
};
 
const noParens = (i, link) => {
  // Expression régulière qui détermine si le texte comporte des parenthèses
  const parensRegex = /^((?!\().)*$/;
  return parensRegex.test(link.children[0].data);
};
 
(async () => {
  const response = await got(vgmUrl);
  const $ = cheerio.load(response.body);
 
  $('a').filter(isMidi).filter(noParens).each((i, link) => {
    const href = link.attribs.href;
    console.log(href);
  });
})();

Ici, vous pouvez observer que l'utilisation des fonctions pour filtrer le contenu est intégrée à l'API de Cheerio. Nous n'avons donc pas besoin de code supplémentaire pour convertir la collection d'éléments en tableau.

Remplacez le code dans index.js par ce nouveau code, et exécutez-le à nouveau. L'exécution devrait être sensiblement plus rapide car Cheerio est une bibliothèque moins lourde.

Si vous souhaitez obtenir des informations plus détaillées, consultez l'autre tutoriel que j'ai rédigé sur l'utilisation de Cheerio.

Puppeteer

Puppeteer est bien plus différent des deux précédents dans le sens où il s'agit principalement d'une bibliothèque pour le headless browser scripting (l’écriture de scripts pour navigateurs sans interface visuelle utilisateur). Puppeteer fournit une API de haut niveau pour contrôler Chrome ou Chromium via le protocole DevTools. Il est beaucoup plus polyvalent car vous pouvez écrire du code pour interagir avec et manipuler des applications web plutôt que de simplement lire des données statiques.

Installez-le avec la commande suivante:

npm install puppeteer@5.5.0

La différence avec le Web scraping via Puppeteer - comparé aux deux outils précédents - est que : plutôt que d'écrire du code pour récupérer le HTML brut d'une URL et le transmettre à un objet, vous écrivez du code qui va s'exécuter dans le contexte d'un navigateur qui traite le HTML d'une URL donnée et construit un véritable modèle document objet (DOM) à partir de celui-ci.

L'extrait de code suivant demande au navigateur de Puppeteer d'aller sur l'URL que nous voulons et d'accéder à tous les éléments d'hyperliens que nous avons analysés précédemment:

const puppeteer = require('puppeteer');
 
const vgmUrl = 'https://www.vgmusic.com/music/console/nintendo/nes';
 
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
 
  await page.goto(vgmUrl);
 
  const links = await page.$$eval('a', elements => elements.filter(element => {
    const parensRegex = /^((?!\().)*$/;
    return element.href.includes('.mid') && parensRegex.test(element.textContent);
  }).map(element => element.href));
 
  links.forEach(link => console.log(link));
 
  await browser.close();
})();

A noter : Nous écrivons toujours une logique pour filtrer les liens sur la page, mais au lieu de déclarer plus de fonctions de filtrage, nous le faisons simplement en ligne. Il y a un peu de code passe-partout impliqué pour dire au navigateur ce qu'il doit faire, mais nous n'avons pas besoin d'utiliser un autre module Node pour faire une requête au site web que nous essayons de scrapper.

Dans l'ensemble, il est beaucoup plus lent si vous faites des choses simples comme celles-ci, mais Puppeteer est très utile si vous avez affaire à des pages qui ne sont pas statiques.

Pour un guide plus complet sur la façon d'utiliser les fonctionnalités de Puppeteer pour interagir avec des applications web dynamiques, voici un de mes autres tutoriels approfondissant le travail avec Puppeteer.

Playwright

Playwright est une autre bibliothèque pour le headless browser scripting (l’écriture de scripts pour navigateurs sans interface visuelle utilisateur), conçue par la même équipe qui a construit Puppeteer. Son API et ses fonctionnalités sont presque identiques à celles de Puppeteer, mais elle a été conçue pour être multi-navigateurs et fonctionne avec FireFox et Webkit ainsi qu'avec Chrome/Chromium.

Installez-le avec la commande suivante :

npm install playwright@0.13.0

Le code pour effectuer cette tâche à l'aide de Playwright est sensiblement le même, à l'exception du fait que nous devons déclarer explicitement le navigateur que nous utilisons:

const playwright = require('playwright');
 
const vgmUrl = 'https://www.vgmusic.com/music/console/nintendo/nes';
 
(async () => {
  const browser = await playwright.chromium.launch();
  const page = await browser.newPage();
 
  await page.goto(vgmUrl);
 
  const links = await page.$$eval('a', elements => elements.filter(element => {
    const parensRegex = /^((?!\().)*$/;
    return element.href.includes('.mid') && parensRegex.test(element.textContent);
  }).map(element => element.href));
 
  links.forEach(link => console.log(link));
 
  await browser.close();
})();

Ce code devrait faire la même chose et se comporter de manière similaire au code de la section Puppeteer.

L'avantage d'utiliser Playwright est qu'il est plus polyvalent car il fonctionne avec plusieurs types de navigateur ! Essayez d'exécuter ce code en utilisant les autres navigateurs et voyez comment cela affecte le comportement de votre script...

Comme pour les autres bibliothèques, un autre tutoriel qui va plus loin dans le travail avec Playwright est à votre disposition si vous cherchez une explication plus fournie.

La vaste étendue du World Wide Web

À présent que vous pouvez récupérer à l’aide de code des éléments de pages web, vous avez accès à une énorme source de données pour tous les besoins de vos projets.

Nota bene : Les modifications apportées au code HTML d'une page web peuvent altérer votre code. Veillez donc à ce que tout soit à jour si vous créez des applications reposant sur le scraping.

Je suis impatient de voir ce que vous allez construire !