如何使用 Node Fetch 代理并构建网络抓取器
米赫内亚-奥克塔维安-马诺拉什(Mihnea-Octavian Manolache),2023 年 4 月 24 日

首先,在构建 Web scraper 时,代理肯定是非常重要的。其次,node-fetch 是最流行的 JavaScript HTTP 客户端之一。因此,将两者结合起来并创建一个网络搜刮器是合情合理的。
这就是为什么我们今天要讨论如何使用代理和 node-fetch。此外,我们还将在这一基础架构之上构建一个网络刮刀。
因此,在本文结束时
- 您将充分了解代理如何在网络搜索中发挥作用
- 您将学习如何将代理与 node-fetch 集成在一起
- 您的个人文件夹中将添加一个项目
什么是代理,为什么使用代理进行网络搜索?
在计算机网络中,代理充当客户端和服务器之间的 "中间件"。代理服务器的结构相当复杂,但从高层来看,这就是使用代理服务器时发生的情况:
- 您 "接入 "代理服务器,并指定您试图访问(搜刮)的目的地(服务器)
- 代理服务器连接目标网站并检索结果(例如网站的 HTML 文件)
- 然后,代理服务器会将从目标服务器得到的响应反馈给你

在这个链条中,你的 IP 始终隐藏在目标服务器中,因为你从未真正连接到它。这也是代理成为网络搜刮重要组成部分的主要原因。它们可以 "隐藏 "网络搜刮者的 IP 地址,因此不会被反机器人系统拦截。
为什么使用 node-fetch 进行网络搜刮?
如果 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 结合使用。因此,如果你想建立一个网络搜刮器,但你的基础架构依赖于节点获取,那么你可能会冒暴露真实 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));
如何使用代理和 node-fetch 构建网络搜索器?
让我们高兴的是,我们已经构建了一个网络搜刮器。上面的代码示例所做的正是所有网络搜索器都会做的事情,即从网站上收集数据。然而,在现实生活中,网络搜刮器要复杂得多。例如,原始数据需要处理,或者我们需要检查和解析收集到的标题或 cookie。因此,让我们深入研究一下,将我们的第一个示例变成一个真正的刮板。让我们设定一些期望值:
- 我们应该能够返回原始 HTML
- 我们应该能够将整个响应作为 JSON 对象返回
- 我们应该能够根据特定的选择器提取元素
假设你已经安装了 node-fetch 和 https-proxy-agent,我们还需要一样东西:HTML 解析器。我一直都在使用 cheerio 来进行网络搜索。所以请确保在你的项目中安装了它。说完这些,让我们开始吧:
#1:导入依赖项
我们要做的第一件事就是导入上面讨论过的软件包。我想这部分不需要进一步解释了:
import fetch from 'node-fetch';
import HttpsProxyAgent from "https-proxy-agent";
import * as cheerio from 'cheerio';
#2:刮板逻辑
我们需要刮擦器能执行三种操作:返回行 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();
}
#3:参数解析器
我们可以使用终端参数来区分我们在刮板中实施的三个选项。在 Node 中,你可以使用一些选项来解析终端参数,但我喜欢一切从简。因此,我们将使用 process.argv,它会生成一个参数数组。请注意,数组的前两项是 "node "和脚本名称。例如,如果运行 `node scraper.js raw_html`,参数数组将如下所示:
[
'/usr/local/bin/node',
'path_too_directory/scraper.js',
'raw_html'
]。
忽略前两个要素,我们将使用以下逻辑:
- 第一个参数将指定我们要运行的函数;
- 第二个将指向我们的目标(我们要抓取的网站);
- 第三个将指向代理服务器;
- 而第四个将指向 css 选择器。
因此,运行刮板的命令应该是这样的:
~ " 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 动态呈现内容,简单的 HTTP 客户端(如 node-fetch)可能不够用。您可能需要探索使用真正的网络浏览器。Python 的 selenium或 Node 的 puppeteer 就是这方面的两种选择。
结论
使用代理和 node-fetch 是构建网络搜刮器的良好开端。不过,您必须考虑到这两者并不 "直接兼容",因此您必须使用第三方解决方案将它们连接起来。幸运的是,有很多可能性,JavaScript 社区总是乐于帮助新手。
不过,制造隐形铲运机的难度更大,你必须想出更复杂的躲避技巧。但我喜欢见机行事。为什么不把今天学到的知识加以补充呢?希望到最后,你能用节点获取和代理编写出最好的网络搜刮程序。一如既往,我对你的建议是不断学习!
新闻和更新
订阅我们的时事通讯,了解最新的网络搜索指南和新闻。
We care about the protection of your data. Read our <l>Privacy Policy</l>.Privacy Policy.
