首先,在构建网页爬虫时,代理无疑非常重要。其次,node-fetch 是最受欢迎的 JavaScript HTTP 客户端之一。因此,将二者结合起来创建网页爬虫是很有意义的。
正因如此,今天我们将探讨如何在 node-fetch 中使用代理。作为额外内容,我们还将基于这一架构构建一个网页爬虫。
读完本文后:
- 您将深入理解代理在网页抓取中的工作原理
- 您将学会如何将代理与 node-fetch 集成
- 您的个人作品集将新增一个项目

首先,在构建网页爬虫时,代理无疑非常重要。其次,node-fetch 是最受欢迎的 JavaScript HTTP 客户端之一。因此,将二者结合起来创建网页爬虫是很有意义的。
正因如此,今天我们将探讨如何在 node-fetch 中使用代理。作为额外内容,我们还将基于这一架构构建一个网页爬虫。
读完本文后:
在计算机网络中,代理充当客户端与服务器之间的“中间件”。代理服务器的架构相当复杂,但从宏观层面来看,使用代理时发生的过程如下:
在此过程中,您的 IP 地址对目标服务器始终处于隐藏状态,因为您实际上从未直接连接到该服务器。这正是代理在网页抓取中如此重要的主要原因。它们“隐藏”了网页抓取工具的 IP 地址,从而避免被反机器人系统封锁。
如果 JavaScript 是您最喜欢的编程语言,您可以使用许多 HTTP 客户端来构建网络爬虫。在最流行的选项中,包括 axios、got 以及此处列出的其他几个。但 node-fetch 仍然是最常被下载的 npm 包之一,这背后是有原因的。
首先,它是首个在 Node.js 中实现 Fetch API 的包。随后,从 v17.5.0 开始,Node.js 团队正式集成了 Fetch API,因此它不再需要作为第三方依赖。但截至 Node.js 19.3.0 版本,fetch 仍被标记为实验性功能。因此,node-fetch 依然是更稳定的解决方案。
就数据抓取而言,node-fetch 是一个绝佳的工具,正如其名,它用于从各种来源获取资源。这或许正是数据抓取最基本的定义。
简而言之,node-fetch 没有内置方法支持代理。因此,如果您想构建一个网络爬虫,但您的基础设施依赖于 node-fetch,那么您可能会面临泄露真实 IP 的风险。这意味着您可能会被反机器人软件封禁。
不过,好在有解决办法。其中一种由 Nathan Rajlich 提供,他开发了一个实现 http.Agent 接口的模块,名为 https-proxy-agent。该模块可通过 npm 安装。此外,将其与 node-fetch 结合使用非常简单:
import fetch from 'node-fetch';
import HttpsProxyAgent from "https-proxy-agent";
const fetch_proxy = async () => {
const proxy = new HttpsProxyAgent('http://1.255.134.136:3128');
const response = await fetch('https://httpbin.org/ip', { agent: proxy});
const data = await response.text();
console.log(data);
}
fetch_proxy()为了进行本次测试,我使用了 Proxy Scrape 提供的免费代理。正如预期的那样,响应信息显示请求源自代理 IP,而非我的本地 IP:
"origin": "1.255.134.136"
另一种方案是使用 node-fetch-with-proxy。该包在 node-fetch 的基础上,将 proxy-agent 作为其自身的依赖项。要使其生效,您只需设置 ‘HTTP_PROXY’ 环境变量即可。该变量的值应为您的代理服务器 IP 地址和端口号。 随后,您可以使用标准的 node-fetch 语法进行请求,这些请求将自动被路由到代理服务器。以下是一个示例:
import fetch from "node-fetch-with-proxy";
fetch('http://httpbin.org/ip')
.then(res => res.json())
.then(json => console.log(json));值得一提的是,我们已经构建了一个网页爬虫。上面的代码示例完全实现了任何网页爬虫的功能,即从网站收集数据。然而,在实际应用中,网页爬虫会稍微复杂一些。例如,需要对原始数据进行处理,或者需要检查并解析收集到的头部信息或 Cookie。因此,让我们深入探讨一下,将我们的第一个示例改造成一个真正的爬虫。 先明确一下目标:
假设您已经安装了 node-fetch 和 https-proxy-agent,我们还需要一个工具:HTML 解析器。我做网页抓取时总是首选 cheerio。因此,请确保在您的项目中安装它。言归正传,让我们开始吧:
首先,我们需要导入上述提到的包。我想这一部分无需多做解释:
import fetch from 'node-fetch';
import HttpsProxyAgent from "https-proxy-agent";
import * as cheerio from 'cheerio';我们需要爬虫能够执行三项操作:返回行级 HTML、返回完整响应,以及根据 CSS 选择器返回元素。其中一项我们之前已经部分实现了。但让我们将所有内容拆分为三个函数:
const raw_html = async (proxyServer, targetURL) => {
const proxy = new HttpsProxyAgent(proxyServer);
const response = await fetch(targetURL, { agent: proxy});
const data = await response.text();
return data;
}
const json_response = async (proxyServer, targetURL) => {
const proxy = new HttpsProxyAgent(proxyServer);
const response = await fetch(targetURL, { agent: proxy});
const data = {
url: response.url,
status: response.status,
Headers: response.headers,
body: await response.text()
}
return data;
}
const select_css = async (proxyServer, targetURL, cssSelector) => {
const proxy = new HttpsProxyAgent(proxyServer);
const response = await fetch(targetURL, { agent: proxy});
const html = await response.text();
const $ = cheerio.load(html);
return $(cssSelector).text();
}我们可以利用终端参数来区分爬虫中实现的这三个选项。 Node.js 中有专门用于解析终端参数的选项,但我倾向于保持简单。因此我们将使用 `process.argv`,它会生成一个参数数组。请注意,该数组的前两个元素是“node”和你的脚本名称。例如,如果运行 `node scraper.js raw_html`,参数数组将如下所示:
[
'/usr/local/bin/node',
'path_to_directory/scraper.js',
'raw_html'
]忽略前两个元素,我们将采用以下逻辑:
因此,运行我们的爬虫的命令应如下所示:
~ » node scraper.js raw_html https://webscrapingapi.com http://1.255.134.136:3128
这简单来说就是从 WebScrapingAPI 的首页提取原始 HTML,并使用 http://1.255.134.136 作为代理中间件。现在最后一步是编写这些参数的逻辑代码,以便我们的代码能够理解该运行命令:
const ACTION = process.argv[2]
const TARGET = process.argv[3]
const PROXY = process.argv[4]
const SELECTOR = process.argv[5]switch (ACTION) {
case 'raw_html':
console.log(await raw_html(PROXY, TARGET))
break
case 'json_response':
console.log(await json_response(PROXY, TARGET))
break
case 'select_css':
SELECTOR ? console.log(await select_css(PROXY, TARGET, SELECTOR)) : console.log('Please specify a CSS selector!')
break
default:
conssole.log('Please choose between `raw_html`, `json_response` and `select_css`')
}基本上就是这样。恭喜!您已成功使用 node-fetch 配合代理创建了一个功能齐全的网页抓取工具。现在,我挑战您为这个抓取工具添加更多功能,打造您自己的版本,并将其作为个人作品集中的亮点。
正如我常说的,隐蔽的爬网远不止于隐藏 IP 地址。实际上,在网页爬虫中使用代理服务器仅仅是抵御反机器人软件的一层防护。 另一层防护是通过[在请求中设置自定义头部](链接 https://trello.com/c/n8xZswSI/14-2-8-january-article-13-http-headers-with-axios)来更改用户代理。
以 Web Scraping API 为例,我们拥有一支专门研究定制规避技术的团队。其中一些技术甚至会修改无头浏览器的默认值,以避免被指纹识别。
此外,由于现代网站使用 JavaScript 动态渲染内容,像 node-fetch 这样的简单 HTTP 客户端可能不够用。您可能需要考虑使用真正的网页浏览器。Python 的 Selenium 或 Node 的 Puppeteer 就是您可以考虑的两个选项。
在构建网络爬虫时,结合 node-fetch 使用代理是一个绝佳的起点。但需注意,二者并非“直接兼容”,你需要借助第三方解决方案来实现连接。所幸可选方案众多,且 JavaScript 社区总是乐于帮助新手。
不过,构建隐蔽的爬虫更为困难,你需要设计出更复杂的规避技术。但我习惯在一切事物中寻找机遇。何不将今天所学加以拓展呢?希望到最后,你能开发出基于 Node-fetch 和代理的最佳网页爬虫。一如既往,我的建议是:持续学习!

Mihnea-Octavian Manolache 是 WebScrapingAPI 的全栈及 DevOps 工程师,负责开发产品功能并维护确保平台平稳运行的基础设施。