首页 > 解决方案 > 使用 fetch 在 Node.js 中获取 URL 脚本内容

问题描述

我正在尝试使用 node-fetch 从 url 获取脚本标记内的内容,然后尝试 json 解析数据,但我一直得到未定义的返回。

我正在尝试从下面的 html 中的变量 game 中获取内容,然后进行字符串化,然后解析 json,但它返回未定义。

页面html:

<!DOCTYPE html>
  <head>
    <meta charset="UTF-8" />
    <title>Document Title</title>
  </head>
  <body>
    <div id="welcome-div">
      <p>Welcome to the my website</p>
    </div>
    <script> 
      var game = new Game({
        stage: "prod",
        servers: {
          v32306117: {
            id: "v32306117",
            name: "name #1",
            hostname: "hostname1",
            port: 80,
          },
          v32306125: {
            id: "v32306125",
            name: "name #2",
            hostname: "hostname2",
            port: 80,
          }
        },
        userGroup: 0
      });

      game.init(function() {
        game.assetManager.load([{
          "name": "\/asset\/image\/map\/grass.png",
          "url": "\/asset\/image\/map\/grass.png"
        }]);

        game.debug.init();
        game.run();
      });

    </script>
  </body>
</html>

抓取功能:

    const fetch = require("node-fetch");

    async function serversFetch() {
      try {
        const servers = await fetch("https://get-servers.herokuapp.com/");
        const data = await servers.text();

        const servers_data = data.substring(
          data.lastIndexOf("var game =") + 20,
          data.lastIndexOf("game.init") - 10
        );

        return JSON.stringify(servers_data);
      } catch (error) {
        console.log(error);
      }
    }

    (async () => {
      const data = await serversFetch();
      console.log('data', data);

      const info = JSON.parse(data);
      console.log('info', info.servers); // returns undefined
    })()

如果我控制台日志 info.servers 它回来未定义但如果我控制台日志只是信息它记录下面的输出。

info {
        stage: "prod",
        servers: {
          v32306117: {
            id: "v32306117",
            name: "name #1",
            hostname: "hostname1",
            port: 80,
          },
          v32306125: {
            id: "v32306125",
            name: "name #2",
            hostname: "hostname2",
            port: 80,
          }
        },
        userGroup: 0
      }

标签: javascriptnode.js

解决方案


您遇到的问题是因为JSON.stringify仅适用于 JavaScript 对象并且servers_data是一个字符串。这导致info后来成为一个字符串,这就是为什么console.log(info.servers)logs undefined。当您检查console.log(info)时,它只具有正常工作的外观,因为它正在记录对象的字符串值。你可以通过执行来测试它console.log(typeof info),你会看到它是 type string

您正在寻找的是servers_data一个有效的 JSON 字符串,而不是一个 JavaScript 对象的字符串(它缺少 JSON 需要的对象属性名称周围的所有双引号)。第一种选择可能是暴力破解它并用双引号括起来的属性替换这些属性,即servers:for "servers:"(包含冒号以使其更加独特,但它仍然不是防弹的)。这不能帮助您处理v32306117可能独特且无法使用正在寻找已知属性的蛮力替换轻松替换的属性。

下一个选项可能是为字符串创建一个解析器,该解析器可以将其解析为 JavaScript 的抽象语法树 (AST)。然后,您可以轻松地将其映射到 JSON 的 AST,然后使用理解 JSON 的 AST 的解析器将其转换为字符串。大多数使用 AST 的解析器可以将字符串解析为 AST 并从 AST 创建字符串。这些解析器通常使用递归下降解析器编写。尽管这对程序员来说是一个很好的练习,但您可能会找到一些为 JavaScript 和 JSON 实现 AST 解析器的库。此外,对于您要完成的工作来说,这有点矫枉过正。

我认为最容易实现和最容易维护的最后一个选项是使用JSON5,它是 JSON 的超集。使用JSON5.parse,您可以按原样解析servers_data,而不必担心 JSON 格式。这是因为 JSON5 接受不带双引号的属性,并且对格式更宽松(双引号与单引号等)。


推荐阅读