Serverseitige Visualisierung mit Nightmare

Avatar of Ashley Davis
Ashley Davis am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Dies ist ein Auszug aus Kapitel 11 von Ashley Davis' Buch Data Wrangling with JavaScript, das jetzt im Manning Early Access Program erhältlich ist. Ich liebe diese Idee absolut, da es im Web so viele Datenvisualisierungs-Elemente gibt, die auf voll funktionsfähigem clientseitigem JavaScript und potenziell mehr API-Aufrufen beruhen. Das ist bei weitem nicht so robust, zugänglich oder syndizierbar, wie es sein könnte. Wenn Sie diese Datenvisualisierung zurück auf den Server verlagern, können Sie progressive Enhancement einbeziehen. Der gesamte Beispielcode und die Daten finden Sie auf GitHub.

Bei explorativer Programmierung oder Datenanalyse in Node.js ist es sehr nützlich, eine Visualisierung aus unseren Daten rendern zu können. Wenn wir im browserbasierten JavaScript arbeiten würden, könnten wir jede beliebige der vielen Chart-, Grafik- und Visualisierungsbibliotheken auswählen. Unter Node.js haben wir leider keine praktikablen Optionen. Wie können wir das also anders erreichen?

Wir könnten versuchen, so etwas wie das Fälschen des DOM unter Node.js zu tun, aber ich habe einen besseren Weg gefunden. Wir *können* unsere browserbasierten Visualisierungsbibliotheken unter Node.js mit einem headless Browser nutzen. Das ist ein Browser, der keine Benutzeroberfläche hat. Sie können sich das wie einen unsichtbaren Browser vorstellen.

Ich verwende Nightmare unter Node.js, um Visualisierungen in PNG- und PDF-Dateien zu erfassen, und es funktioniert wirklich gut!

Der Headless Browser

Wenn wir an einen Webbrowser denken, denken wir normalerweise an die grafische Software, mit der wir im täglichen Leben beim Surfen im Web interagieren. Normalerweise interagieren wir direkt mit einem solchen Browser, sehen ihn mit unseren Augen und steuern ihn mit unserer Maus und Tastatur, wie in Abbildung 1 gezeigt.

Abbildung 1: Der normale Zustand der Dinge: Unsere Visualisierung wird in einem Browser gerendert und der Benutzer interagiert direkt mit dem Browser

Ein Headless Browser hingegen ist ein Webbrowser, der keine grafische Benutzeroberfläche hat und keine direkten Mittel für uns zur Steuerung bietet. Sie fragen sich vielleicht, was nützlich an einem Browser ist, den wir nicht direkt sehen oder mit dem wir nicht interagieren können.

Nun, als Entwickler würden wir typischerweise einen Headless Browser zur Automatisierung und zum Testen von Webseiten verwenden. Nehmen wir an, Sie haben eine Webseite erstellt und möchten eine Reihe von automatisierten Tests darauf ausführen, um zu beweisen, dass sie wie erwartet funktioniert. Die Testsuite ist automatisiert, was bedeutet, dass sie per Code gesteuert wird, und das bedeutet, dass wir den Browser per Code *steuern* müssen.

Wir verwenden einen Headless Browser für automatisierte Tests, da wir die zu testende Webseite nicht direkt sehen oder mit ihr interagieren müssen. Die Beobachtung eines solchen automatisierten Tests während der Ausführung ist unnötig, alles, was wir wissen müssen, ist, ob der Test bestanden oder fehlgeschlagen ist – und wenn er fehlgeschlagen ist, möchten wir wissen, *warum*. Tatsächlich wäre eine GUI für den zu testenden Browser für einen Continuous-Integration- oder Continuous-Deployment-Server, auf dem viele solcher Tests parallel laufen können, sogar ein Hindernis.

Headless Browser werden also oft zum automatisierten Testen unserer Webseiten verwendet, aber sie sind auch unglaublich nützlich, um browserbasierte Visualisierungen zu erfassen und sie als PNG-Bilder oder PDF-Dateien auszugeben. Um dies zu ermöglichen, benötigen wir einen Webserver und eine Visualisierung. Dann müssen wir Code schreiben, um einen Headless Browser zu instanziieren und ihn auf unseren Webserver zu richten. Unser Code weist den Headless Browser dann an, einen Screenshot der Webseite zu machen und ihn als PNG- oder PDF-Datei auf unserem Dateisystem zu speichern.

Abbildung 2: Wir können einen Headless Browser unter Node.js verwenden, um unsere Visualisierung in eine statische Bilddatei zu erfassen

Nightmare ist mein bevorzugter Headless Browser. Es ist eine Node.js-Bibliothek (installiert über npm), die auf Electron basiert. Electron ist ein Framework, das normalerweise zum Erstellen plattformübergreifender Desktop-Anwendungen verwendet wird, die auf Web-Technologien basieren.

Warum Nightmare?

Es heißt Nightmare, aber es ist definitiv kein Albtraum zu benutzen. Tatsächlich ist es der einfachste und bequemste Headless Browser, den ich je benutzt habe. Er enthält automatisch Electron, daher müssen wir, um anzufangen, Nightmare einfach wie folgt in unser Node.js-Projekt installieren

npm install --save nightmare

Das ist alles, was wir brauchen, um Nightmare zu installieren, und wir können es sofort von JavaScript aus verwenden!

Nightmare bringt fast alles mit, was wir brauchen: Eine Skriptbibliothek mit einem eingebetteten Headless Browser. Sie enthält auch den Kommunikationsmechanismus zur Steuerung des Headless Browsers von Node.js aus. Größtenteils ist es nahtlos und gut in Node.js integriert.

Electron basiert auf Node.js und Chromium, wird von GitHub gepflegt und bildet die Grundlage für eine Reihe beliebter Desktop-Anwendungen.

Hier sind die Gründe, warum ich mich für Nightmare gegenüber jedem anderen Headless Browser entscheide

  • Electron ist sehr stabil.
  • Electron hat eine gute Leistung.
  • Die API ist einfach und leicht zu erlernen.
  • Es gibt keine komplizierte Konfiguration (einfach benutzen).
  • Es ist sehr gut in Node.js integriert.

Nightmare und Electron

Wenn Sie Nightmare über npm installieren, wird automatisch eine eingebettete Version von Electron mitgeliefert. Wir können also sagen, dass Nightmare nicht nur eine Bibliothek zur Steuerung eines Headless Browsers ist, sondern effektiv *der* Headless Browser ist. Das ist ein weiterer Grund, warum ich Nightmare mag. Bei einigen anderen Headless Browsern ist die Steuerungsbibliothek getrennt, oder schlimmer noch, sie haben gar keine Node.js-Steuerungsbibliothek. Im schlimmsten Fall müssen Sie Ihren eigenen Kommunikationsmechanismus entwickeln, um den Headless Browser zu steuern.

Nightmare erstellt eine Instanz des Electron-Prozesses mithilfe des Node.js-Moduls child_process. Anschließend verwendet es Interprozesskommunikation und ein benutzerdefiniertes Protokoll, um die Electron-Instanz zu steuern. Die Beziehung ist in Abbildung 3 dargestellt.

Abbildung 3: Nightmare ermöglicht uns die Steuerung von Electron, das als Headless Browser läuft

Unser Prozess: Visualisierungen mit Nightmare erfassen

Wie sieht also der Prozess der Erfassung einer Visualisierung in eine Bilddatei aus? Das ist es, was wir anstreben

  1. Daten beschaffen.
  2. Einen lokalen Webserver starten, um unsere Visualisierung zu hosten.
  3. Unsere Daten in den Webserver einspeisen.
  4. Einen Headless Browser instanziieren und ihn auf unseren lokalen Webserver richten.
  5. Warten, bis die Visualisierung angezeigt wird.
  6. Einen Screenshot der Visualisierung in eine Bilddatei machen.
  7. Den Headless Browser herunterfahren.
  8. Den lokalen Webserver herunterfahren.

Eine Visualisierung zur Darstellung vorbereiten

Das erste, was wir brauchen, ist eine Visualisierung. Abbildung 4 zeigt das Diagramm, mit dem wir arbeiten werden. Dies ist ein Diagramm der jährlichen Durchschnittstemperatur in New York City für die letzten 200 Jahre.

Abbildung 4: Durchschnittliche jährliche Temperatur in New York City für die letzten 200 Jahre

Um diesen Code auszuführen, benötigen Sie Node.js installiert. Für dieses erste Beispiel verwenden wir auch live-server (jeder Webserver ist in Ordnung), um die Visualisierung zu testen (da wir unseren Node.js-Webserver noch nicht erstellt haben). Installieren Sie live-server wie folgt

npm install -g live-server

Dann können Sie das Beispiel-Code-Repo für diesen Blogbeitrag klonen

git clone https://github.com/Data-Wrangling-with-JavaScript/nodejs-visualization-example

Gehen Sie nun in das Repo, installieren Sie die Abhängigkeiten und führen Sie das Beispiel mit live-server aus

cd nodejs-visualization-example/basic-visualization
bower install
live-server

Wenn Sie live-server ausführen, sollte sich Ihr Browser automatisch öffnen und Sie sollten das Diagramm aus Abbildung 4 sehen.

Es ist eine gute Idee, zu überprüfen, ob Ihre Visualisierung direkt in einem Browser funktioniert, bevor Sie versuchen, sie in einem Headless Browser zu erfassen. Es könnte leicht etwas damit nicht stimmen, und Probleme sind in einem echten Browser leichter zu beheben als in einem Headless Browser. live-server hat Live Reload eingebaut, so dass Sie jetzt ein nettes kleines Setup haben, bei dem Sie das Diagramm interaktiv bearbeiten und verbessern können, bevor Sie versuchen, es unter Node.js zu erfassen.

Diese einfache Liniengrafik wurde mit C3 erstellt. Bitte schauen Sie sich den Beispielcode an und vielleicht einige der Beispiele in der C3-Galerie an, um mehr über C3 zu erfahren.

Den Webserver starten

Um unsere Visualisierung zu hosten, benötigen wir einen Webserver. Es reicht nicht aus, dass wir einen Webserver haben, wir müssen ihn auch dynamisch starten und stoppen können. Liste 1 zeigt den Code für unseren Webserver.

Liste 1 – Code für einen einfachen Webserver, der gestartet und gestoppt werden kann

const express = require('express');
const path = require('path');
 
module.exports = {
  start: () => { // Export a start function so we can start the web server on demand.
    return new Promise((resolve, reject) => {
      const app = express();

      const staticFilesPath = path.join(__dirname, "public"); // Make our 'public' sub-directory accessible via HTTP. 
      const staticFilesMiddleWare = express.static(staticFilesPath);
      app.use('/', staticFilesMiddleWare);
     
      const server = app.listen(3000, err => { // Start the web server!
        if (err) {
          reject(err); // Error occurred while starting web server.
        }
        else {
          resolve(server); // Web server started ok.
        }
      });                        
    });
  }
}

Das Code-Modul in Liste 1 exportiert eine Startfunktion, die wir aufrufen können, um unseren Webserver zu starten. Diese Technik, unseren Webserver starten und stoppen zu können, ist auch sehr nützlich für automatisierte Integrationstests einer Webseite. Stellen Sie sich vor, Sie möchten Ihren Webserver starten, einige Tests daran ausführen und ihn dann am Ende stoppen.

Nun haben wir unsere browserbasierte Visualisierung und einen Webserver, der nach Bedarf gestartet und gestoppt werden kann. Das sind die Rohzutaten, die wir für die Erfassung serverseitiger Visualisierungen benötigen. Mischen wir es mit Nightmare!

Das Rendern der Webseite in ein Bild

Nun wollen wir den Code ausarbeiten, um einen Screenshot der Visualisierung mit Nightmare zu erfassen. Liste 2 zeigt den Code, der Nightmare instanziiert, ihn auf unseren Webserver richtet und dann den Screenshot macht.

Liste 2 – Erfassen unseres Diagramms in eine Bilddatei mit Nightmare

const webServer = require('./web-server.js');
const Nightmare = require('nightmare');
 
webServer.start() // Start the web server.
.then(server => {
  const outputImagePath = "./output/nyc-temperatures.png";

  const nightmare = new Nightmare(); // Create the Nightmare instance.
  return nightmare.goto("https://:3000") // Point the browser at the web server we just started.
    .wait("svg") // Wait until the chart appears on screen.
    .screenshot(outputImagePath) // Capture a screenshot to an image file.
    .end() // End the Nightmare session. Any queued operations are completed and the headless browser is terminated.
    .then(() => server.close()); // Stop the web server when we are done.
})
.then(() => {
  console.log("All done :)");
})
.catch(err => {
  console.error("Something went wrong :(");
  console.error(err);
});

Beachten Sie die Verwendung der Funktion goto. Dies ist das, was den Browser tatsächlich anweist, unsere Visualisierung zu laden.

Webseiten brauchen normalerweise einige Zeit zum Laden. Das wird wahrscheinlich nicht sehr lange dauern, besonders da wir einen lokalen Webserver betreiben, aber trotzdem besteht die Gefahr, dass wir einen Screenshot des Headless Browsers vor oder während seines ersten Anzeigeschritts machen. Deshalb müssen wir die Funktion wait aufrufen, um zu *warten*, bis das <svg>-Element des Diagramms im DOM des Browsers erscheint, bevor wir die Screenshot-Funktion aufrufen.

Schließlich wird die Funktion end aufgerufen. Bis zu diesem Zeitpunkt haben wir effektiv eine Liste von Befehlen zum Senden an den Headless Browser erstellt. Die End-Funktion sendet die Befehle tatsächlich an den Browser, der den Screenshot macht und die Datei nyc-temperatures.png ausgibt. Nachdem die Bilddatei erfasst wurde, beenden wir den Prozess, indem wir den Webserver herunterfahren.

Sie finden den vollständigen Code im Unterverzeichnis capture-visualization im Repo. Gehen Sie in das Unterverzeichnis und installieren Sie die Abhängigkeiten

cd nodejs-visualization-example/capture-visualization
cd public 
bower install
cd ..
npm install
live-server

Nun können Sie den Code selbst ausprobieren

node index.js

Dies war ein Auszug aus Kapitel 11 von Data Wrangling with JavaScript, das jetzt im Manning Early Access Program erhältlich ist. Bitte verwenden Sie diesen Rabattcode fccdavis3 für 37% Rabatt. Bitte besuchen Sie The Data Wrangler für neue Updates zum Buch.