logo
Laucher

Laucher

Laucher

2023年 01月 15日

0

最佳 Node.js 网页抓取(爬虫)工具

网页抓取工具是一种从网站和网络API中获取信息的工具或脚本,它可以帮助您提取洞察力或编制包含信息的数据库。例如,像谷歌、百度等这些搜索引擎会抓取网络以对站点进行索引,并将其作为用户查询的结果提供。

封面图

在本文中,我们将探讨一些最佳的 Node.js 网页抓取库和技术。您还将了解它们之间的区别,以便在考虑项目需求时选择合适的工具。首先,我们将回顾一些在选择网页抓取工具之前需要牢记的事项。

在开始进行网页抓取之前需要了解的事项

尽管抓取公开可用信息是合法的,但您应该意识到许多网站在其服务条款中设置了限制。有些甚至可能会包括速率限制,以防止您减慢他们的服务速度 —— 但这是为什么呢?

当您从网站上抓取信息时,您使用了它的资源。

假设您在访问过多页面时过于激进,那么您可能会降低该网站对其用户的总体性能。因此,在进行网页抓取时,您必须获得所有者的同意或许可,并注意您对其网站施加的压力。

最后,网页抓取需要进行大量的开发工作,并且在许多情况下需要进行维护。目标网站结构的变化可能会破坏您的抓取代码,并要求您更新脚本以适应新的格式。

因此,我更倾向于在可能的情况下使用 API,并将网页抓取仅作为最后的选择。现在,让我们开始对最佳的 Node.js 网页抓取库进行评估。

最佳的 Node.js 网页抓取库


Axios

如果您熟悉 Axios,它可能并不是用于网页抓取的最吸引人的选项。尽管如此,它是一个简单的解决方案,可以帮助您完成任务,并且它还具有一个附加优势,即您可能已经相当熟悉这个库。

Axios 是一个基于 Promise 的 Node.js HTTP 客户端,在 JavaScript 项目中因其简单性和适应性而广受欢迎。虽然 Axios 通常用于调用 REST API,但它也可以获取网站的 HTML。

由于 Axios 只会从服务器获取响应,您需要自己解析和处理结果。因此,我建议在处理 JSON 响应或进行简单的抓取时使用此库。

您可以使用您喜欢的包管理器来安装 Axios,步骤如下:

npm install axios

以下是使用 Axios 的示例,从 LogRocket 博客主页列出所有文章的标题: const axios = require(‘axios’);

app.get("/axios", (req, res) => {
    axios
        .get("https://czhlove.cn")
        .then(function (response) {
            const reTitles = /<h2.*?class=".*?MuiTypography-root\sMuiTypography-h2\sMuiTypography-gutterBottom\scss-1dem3ju.*?".*?>(.*?)<\/h2>/g;
            const t = [];
            [...response.data.matchAll(reTitles)].forEach(title => t.push({ 'title': `- ${title[1]}` }));
            res.json(t);
        });
});
/**
[
    {
        "title": "- JavaScript 数组常用方法"
    },
    {
        "title": "- 漫游孤独:一个人的旅行"
    },
    {
        "title": "- TypeScript的装饰器"
    },
    {
        "title": "- TypeScript的命名空间和模块"
    },
    {
        "title": "- TypeScript的高级类型"
    },
    {
        "title": "- TypeScript的泛型"
    },
    {
        "title": "- TypeScript的数组类型"
    },
    {
        "title": "- TypeScript的接口和对象类型"
    },
    {
        "title": "- TypeScript的基础类型"
    },
    {
        "title": "- Vue3的script-setup 模式中父组件获取子组件的数据"
    }
]
 */

在上面的示例中,您可以看到 Axios 在处理 HTTP 请求方面非常出色。然而,解析复杂结构的 HTML 需要详细的规则,甚至对于简单的任务都需要使用复杂的规则或正则表达式。

因此,如果正则表达式不是您的菜,而您更喜欢基于 DOM 的方法,您可以使用诸如 JSDom 或 Cheerio 等库将 HTML 转换为类似 DOM 的对象。让我们使用 JSDom 来探索上面的相同示例:

app.get("/axiosJSDOM", (req, res) => {
    axios
        .get("https://czhlove.cn")
        .then(function (response) {
            const dom = new JSDOM(response.data);
            const t = [];
            [...dom.window.document.querySelectorAll('.MuiTypography-h2')].forEach(el => t.push({ 'title': `- ${el.textContent}` }));
            res.json(t);
        });
});
// 输出结果不变,但是基于 DOM 的方法更简单。

但是,这种解决方案很快会遇到其局限性。例如,您只会从服务器获得原始响应 —— 如果您想要访问的页面上的元素是异步加载的呢?

那么对于单页面应用程序(SPA)呢?在这种情况下,HTML 只是加载在客户端上执行所有渲染工作的 JavaScript 库。或者,如果您遇到这些库强加的限制怎么办?毕竟,它们并不是完整的 HTML/DOM 实现,而只是其中的一个子集。

在这些情况下,或者对于复杂的网站,可能最佳选择是使用其他库采用完全不同的方法。

Puppeteer

Puppeteer 是一个高级的 Node.js API,可以通过代码控制 Chrome 或 Chromium。那么,从网页抓取的角度来看,这对我们意味着什么呢?

使用 Puppeteer,您可以访问一个完整的浏览器,比如在后台以无头模式运行的 Chromium,以便浏览网站并完全渲染样式、脚本和异步信息。

要在项目中使用 Puppeteer,您可以像使用任何其他 JavaScript 包一样进行安装:

npm install puppeteer

现在,让我们看一个 Puppeteer 的实际示例:

const puppeteer = require("puppeteer");

async function parseLogRocketBlogHome() {
    // 启动浏览器
    const browser = await puppeteer.launch();

    // 打开一个新标签
    const page = await browser.newPage(); 


    // 访问该页面并等待,直到网络连接完成
    await page.goto('https://czhlove.cn', { waitUntil: 'networkidle2' });

    // 与DOM交互以检索标题
    const titles = await page.evaluate(() => { 
        // 选择所有指定的标签类的元素
        return [...document.querySelectorAll('.MuiTypography-h2')].map(el => el.textContent);
    });

    // 不要忘记关闭浏览器实例来清理内存
    await browser.close();

    // 打印结果
    titles.forEach(title => console.log(`- ${title}`))
    // - JavaScript 数组常用方法
    // - 漫游孤独:一个人的旅行
    // - TypeScript的装饰器
    // - TypeScript的命名空间和模块
    // - TypeScript的高级类型
    // - TypeScript的泛型
    // - TypeScript的数组类型
    // - TypeScript的接口和对象类型
    // - TypeScript的基础类型
    // - Vue3的script-setup 模式中父组件获取子组件的数据
}

parseLogRocketBlogHome();

虽然 Puppeteer 是一个很棒的解决方案,但它在使用上更复杂,特别是对于简单的项目。从资源需求的角度来看,它要求更高 — 毕竟,您正在运行一个完整的 Chromium 浏览器,而我们知道这些浏览器可能会消耗大量内存。

X-Ray

X-Ray 是一个专为网页抓取而创建的 Node.js 库,因此它的 API 在很大程度上专注于这个任务。因此,它将我们在 Puppeteer 和 Axios 中遇到的大部分复杂性都进行了抽象。

要安装 X-Ray,您可以运行以下命令:

npm install x-ray

现在,让我们使用 X-Ray 来构建我们的示例:

const Xray = require('x-ray');
const x = Xray()

x('https://czhlove.cn', {
    titles: ['.card-title a']
})((err, result) => {
    result.titles.forEach(title => console.log(`- ${title}`));
});
// 打印结果:
// - JavaScript 数组常用方法
// - 漫游孤独:一个人的旅行
// - TypeScript的装饰器
// - TypeScript的命名空间和模块
// - TypeScript的高级类型
// - TypeScript的泛型
// - TypeScript的数组类型
// - TypeScript的接口和对象类型
// - TypeScript的基础类型
// - Vue3的script-setup 模式中父组件获取子组件的数据

如果您的使用情景涉及大量网页抓取,X-Ray 是一个很好的选择。它支持并发和分页,因此您不需要担心这些细节。

Superagent

Superagent 是一个轻量级、渐进式的用于处理 HTTP 请求的客户端 Node.js 库。由于其简单性和易用性,它通常被用于网页抓取。

与 Axios 类似,Superagent 也仅限于从服务器获取响应;您需要自己解析和处理结果。根据您的抓取需求,您可以使用 Superagent 获取 HTML 页面、JSON 数据或其他类型的内容。

要在项目中使用 Superagent,您可以像使用任何其他 JavaScript 包一样进行安装:

npm install superagent

在抓取 HTML 页面时,您必须解析 HTML 内容以提取所需数据。为此,您可以使用像 Cheerio 或 JSDOM 这样的库。

要在项目中使用 Cheerio,您可以像使用任何其他 JavaScript 包一样进行安装:

npm install cheerio

让我们来看一个使用 Superagent 和 Cheerio 进行网页抓取的示例:

const superagent = require("superagent");
const cheerio = require("cheerio");
const url = "https://czhlove.cn";

superagent.get(url).end((err, res) => {
  if (err) {
    console.error("获取网站时出错:", err);
    return;
  }
  const $ = cheerio.load(res.text);
  // 请将以下选择器替换为您想要抓取的实际 HTML 元素:
  const titles = $(".MuiTypography-h2")
    .map((i, el) => $(el).text())
    .get();
  // 打印抓取的数据:
  console.log("Titles:", titles);
});
// 打印结果:
// Titles: [
//   'JavaScript 数组常用方法',
//   '漫游孤独:一个人的旅行',
//   'TypeScript的装饰器',
//   'TypeScript的命名空间和模块',
//   'TypeScript的高级类型',
//   'TypeScript的泛型',
//   'TypeScript的数组类型',
//   'TypeScript的接口和对象类型',
//   'TypeScript的基础类型',
//   'Vue3的script-setup 模式中父组件获取子组件的数据'
// ]

该脚本将使用 Superagent 进行 HTTP GET 请求到指定的 URL,获取页面的 HTML 内容,然后使用 Cheerio 从指定的选择器中提取数据。

虽然 Superagent 是一个很好的解决方案,但在进行网页抓取时使用它可能会导致数据提取不完整或不准确,从而导致数据不一致,这取决于网站结构的复杂性和所使用的解析方法。

Playwright

Playwright 是一个强大的工具,用于网页抓取和浏览器自动化,尤其是在处理具有动态内容和复杂交互的现代网页应用程序时。其多浏览器支持、自动化能力和性能使其成为开发人员在 Node.js 应用程序中执行高级网页抓取任务的绝佳选择。

Playwright 是由微软开发的相对较新的开源库。它提供对浏览器状态、Cookie、网络请求和浏览器事件的完全控制,使其在复杂的抓取场景中非常适用。

要在项目中使用 Playwright,您可以按照以下方式进行安装:

npm install playwright

让我们来看一个使用 Playwright 进行网页抓取的示例:

const { chromium } = require("playwright");
(async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();
  const url = "https://czhlove.cn"; // 请将其替换为您想要抓取的网站的 URL。
  try {
    await page.goto(url);
    // 请将以下选择器替换为您想要抓取的实际 HTML 元素。
    const titleElement = await page.$(".MuiTypography-h2");
    const title = await titleElement.textContent();

    console.log("Title:", title);
  } catch (error) {
    console.error("Error:", error);
  } finally {
    await browser.close();
  }
})();

这个脚本将会启动一个 Chromium 浏览器,导航到指定的 URL,并使用 Playwright 的方法与网站进行交互,并从指定的选择器中提取数据。

Playwright 是一个强大的抓取库,但与轻量级的基于 HTTP 的抓取库相比,它会产生更多的资源开销,因为它使用无头浏览器来执行抓取任务。这可能会影响性能和内存使用,特别是如果您正在抓取多个页面或执行大量的抓取任务。

哪个是最好的 Node.js 网络爬虫?

“最佳” Node.js 网络爬虫取决于您的特定需求以及您打算执行的爬取任务的复杂性。提到的每个库 - Axios、X-Ray、Puppeteer、Superagent、Cheerio 和 Playwright - 都有其自身的优点和缺点。以下是它们主要特点的简要概述:

  1. Axios:适用于基本的网络请求,简单且轻量。适合涉及进行HTTP请求的简单爬取任务。

  2. X-Ray:适用于爬取多个网页,具有内置的并发和分页支持。与Puppeteer和Playwright相比,提供更简单的API。

  3. Puppeteer:适用于浏览器自动化,特别是对于动态和复杂的网站。可以处理现代的、JavaScript-heavy的网站并执行交互。

  4. Superagent:轻量级的HTTP库,用于处理请求。对于基本的爬取任务可能会有用。

  5. Cheerio:优秀用于解析HTML并从DOM中提取数据。不需要浏览器,适用于较简单的爬取任务,可以高效地工作。

  6. Playwright:适用于爬取具有复杂交互的现代Web应用程序。提供完整的浏览器控制,适用于更高级的爬取任务。

对于大多数任务来说,任何这些选项都足够满足要求,所以请选择您感觉最舒适的选项。在我的职业生涯中,我有机会使用公开可用信息和内部系统从头构建多个项目,以满足信息收集的需求。

由于需求各异,这些项目中的每一个都采用了不同的方法和库,从Axios到X-Ray不一而足,最终在最复杂的情况下采用了Puppeteer。

最后,无论您选择哪个爬虫,都应始终尊重网站的条款和条件。数据爬取可以是一个强大的工具,但这也伴随着巨大的责任。感谢您的阅读!

评论