使用 Nightmare 进行服务器端可视化

Avatar of Ashley Davis
Ashley Davis

DigitalOcean 提供适用于您旅程各个阶段的云产品。立即开始使用 $200 免费积分!

这是 Ashley Davis 的书籍 Data Wrangling with JavaScript 第 11 章的摘录,现已在 Manning Early Access Program 上提供。我非常喜欢这个想法,因为网络上有很多数据可视化内容依赖于完全运行的客户端 JavaScript 以及可能更多的 API 调用。它不像它本可以的那样健壮、易访问或可联合。如果您将数据可视化带回到服务器,就可以将渐进增强引入进来。 所有示例代码和数据 都可以在 GitHub 上找到。

在使用 Node.js 进行探索性编码或数据分析时,能够从我们的数据中渲染可视化非常有用。如果我们在基于浏览器的 JavaScript 中工作,我们可以选择许多图表、图形和可视化库中的任何一个。不幸的是,在 Node.js 下,我们没有任何可行的选择,那么我们还能如何实现这一点呢?

我们可以尝试在 Node.js 下伪造 DOM,但我发现了一个更好的方法。我们可以使用无头浏览器让我们的基于浏览器的可视化库在 Node.js 下为我们工作。这是一种没有用户界面的浏览器。您可以把它想象成一个不可见的浏览器。

我在 Node.js 下使用 Nightmare 将可视化捕获到 PNG 和 PDF 文件,它非常有效!

无头浏览器

当我们想到网络浏览器时,我们通常会想到我们在日常浏览网络时与之交互的图形软件。通常,我们直接与这样的浏览器交互,用我们的眼睛查看它,并用鼠标和键盘控制它,如图 1 所示。

图 1:正常情况:我们的可视化在浏览器中渲染,用户直接与浏览器交互

另一方面,无头浏览器是一种没有图形用户界面,也没有直接控制手段的网络浏览器。您可能会问,一个我们无法直接看到或与之交互的浏览器有什么用呢?

好吧,作为开发人员,我们通常会使用无头浏览器来自动化和测试网站。假设您创建了一个网页,并且您想要对它运行一套自动化测试,以证明它按预期工作。测试套件是自动化的,这意味着它由代码控制,这意味着我们需要从代码驱动浏览器。

我们使用无头浏览器进行自动化测试,因为我们不需要直接查看或与正在测试的网页进行交互。查看此类正在进行的自动化测试是不必要的,我们只需要知道测试是通过还是失败——如果失败,我们想知道原因。实际上,在持续集成或持续部署服务器中,具有被测浏览器的 GUI 实际上会是一个障碍,因为在这种服务器中,许多这样的测试可以并行运行。

因此,无头浏览器通常用于网页的自动化测试,但它们在捕获基于浏览器的可视化并将它们输出到 PNG 图像或 PDF 文件方面也极其有用。为了使它工作,我们需要一个网络服务器和一个可视化,然后我们必须编写代码来实例化一个无头浏览器并将其指向我们的网络服务器。我们的代码然后指示无头浏览器截取网页的屏幕截图并将其保存到我们的文件系统中,作为 PNG 或 PDF 文件。

图 2:我们可以在 Node.js 下使用无头浏览器将我们的可视化捕获到静态图像文件中

Nightmare 是我选择的无头浏览器。它是一个 Node.js 库(通过 npm 安装),构建在 Electron 之上。Electron 是一个框架,通常用于构建基于 Web 技术的跨平台桌面应用程序。

为什么选择 Nightmare?

它被称为 Nightmare,但使用起来绝对不是噩梦。事实上,它是我用过的最简单、最方便的无头浏览器。它自动包含 Electron,因此要开始使用,我们只需将 Nightmare 安装到我们的 Node.js 项目中,如下所示

npm install --save nightmare

这就是我们安装 Nightmare 所需的全部,我们可以立即从 JavaScript 中开始使用它!

Nightmare 拥有我们几乎需要的一切:一个带有嵌入式无头浏览器的脚本库。它还包括控制无头浏览器与 Node.js 之间通信的机制。在大多数情况下,它是无缝且与 Node.js 很好地集成。

Electron 基于 Node.js 和 Chromium,由 GitHub 维护,是许多流行的桌面应用程序的基础。

以下是我选择使用 Nightmare 而不是其他任何无头浏览器的原因

  • Electron 非常稳定。
  • Electron 具有良好的性能。
  • API 简单易学。
  • 没有复杂的配置(只需开始使用它)。
  • 它与 Node.js 非常好的集成。

Nightmare 和 Electron

当您通过 npm 安装 Nightmare 时,它会自动附带一个嵌入式版本的 Electron。因此,我们可以说 Nightmare 不仅仅是一个用于控制无头浏览器的库,它实际上就是无头浏览器。这也是我喜欢 Nightmare 的另一个原因。在其他一些无头浏览器中,控制库是分开的,或者更糟糕的是,它们根本没有 Node.js 控制库。在最坏的情况下,您必须自己滚动通信机制来控制无头浏览器。

Nightmare 使用 Node.js child_process 模块创建 Electron 进程的实例。然后它使用进程间通信和自定义协议来控制 Electron 实例。关系如图 3 所示。

图 3:Nightmare 允许我们控制作为无头浏览器运行的 Electron

我们的流程:使用 Nightmare 捕获可视化

那么捕获可视化到图像文件的过程是什么?这是我们的目标

  1. 获取数据。
  2. 启动本地 Web 服务器来托管我们的可视化
  3. 将我们的数据注入 Web 服务器
  4. 实例化一个无头浏览器并将其指向我们的本地 Web 服务器
  5. 等待可视化显示
  6. 将可视化的屏幕截图捕获到图像文件中
  7. 关闭无头浏览器
  8. 关闭本地 Web 服务器

准备要渲染的可视化

我们首先需要的是一个可视化。图 4 显示了我们将要使用的图表。这是一个过去 200 年纽约市年平均气温的图表。

图 4:过去 200 年纽约市的年平均气温

要运行此代码,您需要安装 Node.js。对于第一个示例,我们还将使用 live-server(任何 Web 服务器都可以)来测试可视化(因为我们还没有创建我们的 Node.js Web 服务器),按照如下步骤安装 live-server

npm install -g live-server

然后,您可以克隆此博客文章的示例代码存储库

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

现在进入存储库,安装依赖项并使用 live-server 运行示例

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

当您运行 live-server 时,您的浏览器应该自动打开,您应该会看到图 4 中的图表。

在尝试在无头浏览器中捕获可视化之前,最好检查您的可视化是否在浏览器中直接运行;它可能很容易出现问题,而问题在真实浏览器中比在无头浏览器中更容易解决。live-server 内置了实时重载功能,因此现在您有一个很好的设置,您可以交互式地编辑和改进图表,然后再尝试在 Node.js 下捕获它。

这个简单的折线图是用 C3 构建的。请查看示例代码,并可能查看 C3 画廊 中的一些示例,以了解有关 C3 的更多信息。

启动 Web 服务器

为了托管我们的可视化效果,我们需要一个 Web 服务器。仅仅有一个 Web 服务器是不够的,我们还需要能够动态地启动和停止它。清单 1 显示了我们的 Web 服务器代码。

清单 1 - 可启动和停止的简单 Web 服务器的代码

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

清单 1 中的代码模块导出一个 start 函数,我们可以调用它来启动我们的 Web 服务器。这种技术,能够启动和停止我们的 Web 服务器,对于对网站进行自动化集成测试也非常有用。想象一下,您想启动 Web 服务器,对其运行一些测试,然后在结束时停止它。

现在我们有了基于浏览器的可视化效果,还有一个可以按需启动和停止的 Web 服务器。这些是我们捕获服务器端可视化效果所需的原始成分。让我们用 Nightmare 混合一下!

将网页渲染为图像

现在让我们完善代码,使用 Nightmare 捕获可视化效果的屏幕截图。清单 2 显示了实例化 Nightmare、将其指向我们的 Web 服务器,然后拍摄屏幕截图的代码。

清单 2 - 使用 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("http://localhost: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);
});

注意 `goto` 函数的使用,它实际上是将浏览器定向到加载我们的可视化效果。

网页通常需要一些时间才能加载。这可能不会很长,特别是当我们运行本地 Web 服务器时,但我们仍然面临着在无头浏览器初始绘制之前或期间拍摄屏幕截图的风险。这就是为什么我们必须调用 `wait` 函数来 *等待* 直到图表 `` 元素出现在浏览器的 DOM 中,然后我们才能调用屏幕截图函数。

最终,会调用 `end` 函数。到目前为止,我们实际上已经构建了一个要发送到无头浏览器的命令列表。end 函数实际上将命令发送到浏览器,它会拍摄屏幕截图并输出 `nyc-temperatures.png` 文件。在捕获图像文件后,我们将通过关闭 Web 服务器来结束。

您可以在存储库的 capture-visualization 子目录 下找到完成的代码。进入子目录并安装依赖项

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

现在您可以自己尝试代码

node index.js

这是 *Data Wrangling with JavaScript* 第 11 章的摘录,现在可以在 Manning Early Access Program 上获得。请使用此折扣代码 *fccdavis3* 获取 37% 的折扣。请查看 The Data Wrangler 以获取有关本书的最新更新。