发送 HTTP 请求是任何现代编程语言最重要的功能之一。Node.js 也不例外,但直到现在,这一功能仍被市面上数量庞大的 npm 包所垄断。Node-Fetch 提供了一种替代方案,它利用了当前大多数浏览器都支持的原生 fetch API。
Node-Fetch API 简介
在介绍 Node-Fetch API 之前,我必须先简要说明什么是 HTTP 请求。HTTP 请求的目的是从互联网上的 URL 获取信息。访问网站就是 HTTP 请求的一个简单示例。
虽然很久以前,HTTP 请求是通过 XMLHttpRequest 或 XHR 对象实现的,但如今所有现代浏览器都支持 JavaScript 中的 Fetch API。这使得程序员能够使用更简单、更简洁的语法来发送请求。然而,Fetch API 在 Node.JS 这种服务器端语言中缺失了很长时间,这为其他自定义包提供了机会来处理这一功能,例如:Axios、GOT 以及许多其他包。
Node-Fetch 相当于 JavaScript 中的 Fetch API,如今它终于也登陆了 Node.JS。
使用 Node-Fetch API 的先决条件
首先,既然这是一篇 Node.JS 教程,您当然需要安装 Node.JS。如果您尚未安装,可通过此链接下载并安装。
Node.js 仅从 17.5 版本开始提供对 Fetch API 的实验性支持。因此,您需要至少安装 Node 17.5 版本。此外,在运行脚本时,您需要使用 –experimental-fetch 参数。
如果您使用的 Node.JS 版本较低,可以使用 n 包升级到最新版本。n 是一个 npmjs 包,其唯一目的是让您在 Node 和 npm 版本之间进行切换。要安装它并切换到最新版本,请按照以下步骤操作:
npm install -g nn latest
执行 `n latest` 命令将安装最新版本的 Node.js。要验证您的 Node.js 版本,只需运行以下命令:
node –version
如何使用 Node-Fetch
在任何编程语言中,发起 HTTP 请求都是一项异步操作,因为接收请求响应需要时间。关于如何处理异步操作,主要有两种方法:您可以选择等待响应后再继续执行代码,或者让代码并行运行。
Node-Fetch 同时支持同步和异步函数调用。
Node-Fetch 中的 GET 请求
要发送一个简单的 GET 请求并从响应中提取正文,可以使用下面的代码片段:
fetch('https://www.webscrapingapi.com/')
.then((response) => response.text())
.then((body) => {
console.log(body);
});要运行此代码,请将其保存为 node-fetch-example.js 文件,并使用以下命令在同一文件夹中运行:node --experimental-fetch node-fetch-example.js。请注意,运行时会出现一条警告,提示“Fetch API 是一项实验性功能”。这是正常的,因为在撰写本文时,这确实是一项实验性功能。
上述代码段在继续执行前不会等待请求完成。这意味着该代码下方的任何代码都会立即开始执行,而不会等待 fetch 操作结束。例如,如果您在代码下方添加 console.log("Something");,执行脚本时输出结果将如下所示:
为进一步解释上述代码,您可能会注意到我们使用了两次“then”函数。第一个“then”将在收到 HTTP 请求的响应时执行,其作用是将该响应与 response.text() 方法(该方法返回响应正文)的内容进行映射。 但是,response.text() 方法本身也是异步的,因此我们需要在第二个“then”中等待其响应,其中 body 等于 response.text() 承诺的结果。
你也可以像以下示例中那样,使用 await 调用 fetch API:
(async () => {
const response = await fetch('https://webscrapingapi.com');
const body = await response.text();
console.log(body);
})();这进一步阐释了 fetch API 的工作原理以及你需要等待哪些 Promise。在本文后续内容中,我们将结合 await 使用 fetch API,因为这能让代码语法更加简洁。
向请求发送头部
发送请求时,您还需要能够设置请求的头部。为此,您可以在 fetch API 的第二个参数中添加头部,如下所示:
(async () => {
const response = await fetch('http://httpbin.org/headers', {
headers: {
'my-custom-header': 'my-header-value'
}
});
const body = await response.text();
console.log(body);
})();除了请求头之外,你还可以通过 fetch API 的第二个参数发送许多其他选项。要查看所有选项,请查阅 fetch API 的文档(即客户端使用的版本)。
Node-Fetch 中的 POST 请求
fetch API 的另一个重要选项是 method 选项。它指定了 HTTP 请求所使用的方法。共有 5 种可用方法:GET、POST、PUT、PATCH 和 DELETE,其中前两种(GET 和 POST)最为常用。默认情况下,如果未指定方法,Node-Fetch 将使用 GET。
要使用 Node-Fetch 发送 POST 请求,可以使用以下代码片段:
(async () => {
const response = await fetch('http://httpbin.org/post', {
method: 'POST',
body: JSON.stringify({
'key': 'value'
})
});
const body = await response.text();
console.log(body);
})();您可能会注意到,这里我们使用 JSON.stringify 来发送请求正文。这是因为 fetch API 会将正文作为字符串发送,而非像 axios 等其他包那样发送对象。
fetch API 同时也支持所有其他请求方法。
Node-Fetch 中的错误处理
在进行 HTTP 请求时处理错误是必不可少的,因为你永远无法指望第三方服务始终可用。作为最佳实践,你应该始终处理错误,以防止你的应用程序或脚本因请求的 URL 不可用而崩溃。
在 Node-Fetch 中,可以通过简单的 try-catch 语法来处理错误。以下是使用 await 时的示例:
(async () => {
try {
const response = await fetch('[INVALID_URL]');
const responseBody = await response.text();
} catch (error) {
console.log(error.message);
}
})();如果您更倾向于在不使用 await 的情况下使用 fetch,则可以像这样在代码中添加 catch 语句:
fetch('[INVALID_URL]')
.then((response) => response.text())
.then((body) => {
console.log(body);
})
.catch((error) => {
console.log(error.message);
});Node-Fetch 的应用场景
发送 HTTP 请求在许多方面都非常有用,因为它允许从不同的服务中获取新信息,并以相当优雅且简单的方式提取数据。下面我们将探讨几个相关的用例。
使用 Node-Fetch 进行 API 请求
在编程过程中,你经常需要使用 API。这是因为你可能需要从不同的后端源获取特定数据,然后对其进行处理或更新。一个很好的例子是拥有 4 个端点的 API,它允许你在另一台服务器的后端数据库中对用户进行创建、读取、更新和删除(CRUD 操作)。
通常,此类 API 需要身份验证,以防止未经授权的来源使用它并篡改数据以谋取私利。HTTP 请求提供了多种身份验证方法。最常见的一种是使用 API 密钥,即 API 提供商给你一个只有你才知道的密钥,而 API 的端点仅在发送正确密钥时才生效。
API 保护的另一种方法是基本认证(Basic Authentication)。这意味着访问 API 时,您需要在请求头中发送一个格式为“username:password”的 base64 编码字符串。以下是一个在 POST 请求中使用基本认证的示例:
(async () => {
const response = await fetch('http://httpbin.org/post', {
method: 'POST',
headers: {
"Authorization": `Basic ${btoa('login:password')}`
},
body: JSON.stringify({
'key': 'value'
})
});
const body = await response.text();
console.log(body);
})();使用 Node-Fetch 进行网页抓取
网页抓取是一种从网站获取内容并进行解析的方法,以便您仅保留所需数据,并按需使用。cheerio 是一个优秀的 npmjs 库,可帮助您更轻松地解析数据。该库允许您像在 JavaScript 或 jQuery 中查询静态 HTML 一样,对通过 fetch API 获取的静态 HTML 进行查询。
以下是一个使用 fetch API 和 cheerio 获取页面标题的示例:
const cheerio = require("cheerio");
(async () => {
const response = await fetch('https://www.webscrapingapi.com/');
const responseBody = await response.text();
const $ = cheerio.load(responseBody);
console.log($('title').first().text());
})();上述示例应返回“WebScrapingAPI | All-In-One Scraping API”,因为这是页面的标题(即浏览器窗口顶部显示的文本)。 具体来说,我们使用 fetch 从 https://www.webscrapingapi.com/ 获取 HTML 页面源代码,并使用 cheerio 解析该内容。如需进一步了解 cheerio,可在此处查阅其文档
从其他网站抓取信息在许多方面都很有用。例如,抓取的信息可用于创建机器学习模型的训练数据集,或开发价格比较工具——该工具从多个来源提取数据并进行对比。
虽然上面的示例运行良好,但在进行数据抓取时,fetch API 未必总是最佳选择。这是因为如今的现代网站通常通过 JavaScript 展示内容,并使用验证码或其他方法来防止数据被抓取。fetch API 相当于对指定 URL 执行简单的 CURL 请求,它仅获取页面加载时显示的静态内容,完全不包含任何 JavaScript 渲染。
若要在抓取数据的同时执行页面上的 JavaScript 代码,建议参考本文中关于高级抓取的介绍,了解 Puppeteer 等替代方案。不过,如果您不想经历这些繁琐步骤,可以使用 WebScrapingAPI——这是一个专为该任务设计的 API,它能解决所有这些问题(包括反机器人检测),并提供包含所有功能的免费试用版。
总结
总而言之,好消息是期待已久的 fetch API 终于在 Node.js 中可用了,尽管目前它仍处于实验性功能阶段(本文撰写之时)。虽然此前在 Node.js 中也能发起请求,但唯一的方法是通过 XMLHttpRequest/XHR 对象,或是使用 Axios 或 GOT 等众多现有包之一。
这一变化将使客户端 JavaScript 与服务器端的 Node.js 更加趋同,因为该功能在客户端 JavaScript 中早已存在,并得到所有现代浏览器的支持。
发起 HTTP 请求在许多方面都很有用,例如调用 API 或从网站抓取数据。虽然其他 npm 包仍将作为备选方案,并在旧版项目中继续使用,但使用 fetch 是面向未来的最佳解决方案。这将提高 Node.js 代码的可读性,并使从前端到后端的转换变得更加容易。




