如何使用Express.js、Node.js和Cheerio构建网络搜刮器

433 阅读6分钟

如何使用Express.js、Node.js和Cheerio构建网络搜刮器

作为开发者,我们可能会被要求从一个没有API的网站上获取数据。有些网站允许通过 "Web Scraping "过程无限制地提取数据,而另一些网站则对可提取的数据有限制。无论是哪种情况,都应该了解并遵守网站的法律政策。

网络刮削有助于实现自动化任务,例如取代手动列出网站产品的繁琐过程,提取下拉列表中所有国家的国家代码,以及更多。这个过程有利于数据科学家,使其更容易提取和组织表格中的数据以进行适当的分析。

软件开发者也可以将这些数据转换为API。在本教程中,你将建立一个网络搜刮器,从一个加密货币网站上提取数据,并在浏览器中以API的形式输出数据。你将使用Node.js、Express和Cheerio来构建这个刮削工具。

先决条件

你将需要以下条件来了解和构建。

  • 一个已安装的IDE。
  • 一个良好的互联网连接。
  • 具有JavaScript的基本知识。
  • 有安装好的Node.js。

检查网站搜刮的权限

当你想搜刮一个网站时,首先要考虑的应该是检查它是否授予了搜刮的权限,以及哪些行为是不允许的。在网站前面放上一个robots.txt ,像这样。

https://coinmarketcap.com/robots.txt 应该得到下面的结果。

robots

从上面的图片来看,你有权限从主页上刮取数据,但它不允许你刮取个别货币页面中的一些标签。

创建项目

对于这个项目,你将在你的windows explorer中创建一个新的文件夹。将其命名为Custom Web Scraper或任何你喜欢的名字。在VScode中打开该文件夹,此时它应该是空的,在向你的项目添加必要的文件之前,你需要确保Node.js已经安装。

Node.js是一个支持在终端运行JavaScript代码的服务器环境,服务器将用它来创建。现在你已经安装了Node.js,你可以使用Node Package Manager(NPM),在你的VScode中打开终端,然后运行。

cd Custom Web Scraper

这将带你到当前的项目目录,接下来进入。

npm init

上面的命令初始化了一个项目,并创建了一个package.json文件,你所安装的包将被保存在这里。点击回车,package.json 文件将被创建。你会得到一些关于你希望文件包含的信息的提示。注意创建的入口点 -index.js 。你的项目现在包含一个package.json文件,打开它,其中的字段应该是这样的。

{
  "name": "custom-web-scraper",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": ""
  },
  "author": "",
  "license": "ISC"
}

现在你在package.json 文件中已经有了你的入口点index.js ,创建一个新的文件并命名为index.js 。这就是你的代码将被写入的地方。

安装软件包

  • 安装ExpressJs。ExpressJs是Node.js的一个后端框架。你将安装它来监听PORTS,即你为你的服务器设置的端口。要检查一切是否完美。继续并运行。
npm i express

上面的命令为你的项目安装了Express依赖。

  • 安装Cheerio。Cheerio有助于解析标记,它用于从网页中挑出HTML元素。它提供了一个API,允许你操作产生的数据结构。

运行。

npm i cheerio

这将在package.json文件中安装Cheerio的依赖性。

  • 安装Axios。Axios是用来进行HTTP请求的。运行下面的命令来安装这个依赖项。
npm i axios
  • 安装Nodemon。Nodemon是一个工具,当节点应用程序被改变时,它可以帮助重新加载。

现在运行。

npm i nodemon

打开package.json 文件,查看已安装的软件包。

{
  "name": "custom-web-scraper",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.24.0",
    "cheerio": "^1.0.0-rc.10",
    "express": "^4.17.2",
    "nodemon": "^2.0.15"
  }
}

依赖项字段包含了你已经安装的包和它们的版本。同时,编辑脚本,使用nodemon监听index.js文件的变化。

编辑JavaScript文件

为了导入你的软件包,使用require() 函数。编辑index.js 文件,使其看起来像这样。

const express = require("express");
const axios = require("axios");
const cheerio = require("cheerio");

接下来,初始化express,使其监听你要使用的端口。假设你决定使用PORT: 5000 ,你应该能够知道服务器是否正在运行。

编辑index.js文件,看起来像这样。

const PORT = 5000;
const axios = require('axios');
const cheerio = require('cheerio');
const express = require('express');

const app = express();
app.listen(PORT, () => console.log(`server running on port ${PORT}`));

要检查你的服务器是否在指定的端口上运行,请运行。

npm run start

终端上的显示应该是这样的。

port

棒极了!它起作用了。

注意:当你对你的脚本进行修改时,你不一定要输入npm run start ,nodemon会在你保存修改时负责重新加载。

现在专注于实际的搜刮,获得你想搜刮的网站的网址,在这个例子中是Coin Market的网站。Axios获取这个网址,发出HTTP请求,然后返回一个响应数据。这个响应数据可以在终端显示。

在你的index.js中实现这个效果。

const url = "https://coinmarketcap.com/";
axios(url).then((response) => {
  const html_data = response.data;
  const $ = cheerio.load(html_data);
});

从上面的代码中,你会注意到,从HTTP请求中得到的响应被分配到变量html_data

了解Cheerio

在上面的代码片段中,你使用.load() 方法将HTML元素加载到Cheerio中,并将其存储在与jQuery类似的$ 变量中。随着元素的加载,你可以根据你需要的数据检索DOM元素。

Cheerio使浏览DOM元素并对其进行操作成为可能,这是通过针对标签、类、id和hrefs来实现的。例如,一个类为submitButton的元素可以表示为(.submitButton)id('.submitButton'),id为('#submitButton'),还可以通过使用$('h1')挑选一个h1元素。Cheerio提供了一些方法,如find() 来寻找元素,each() 来迭代元素,filter() 方法等等。

用Cheerio解析HTML

在解析一个HTML页面之前,你必须首先检查该页面的结构。在这种情况下,你要选取每个硬币的名称,它的当前价格,以及其他相关数据。

右键点击Coin Market的页面,你会发现数据被存储在一个表中,你会发现在tbody 标签内有一个行的列表tr 。在tr 元素上点击右键,然后点击copy selector

接下来,编辑index.js 文件,使其类似于这样。

  const selectedElem =
      '#__next > div > div.main-content > div.sc-57oli2-0.comDeo.cmc-body-wrapper > div > div:nth-child(1) > div.h7vnx2-1.bFzXgL > table > tbody > tr';
    const keys = [
      'No.',
      'Coin',
      'Price',
      '24h',
      '7d',
      'Marketcap',
      'Volume',
      'CirculatingSupply',
    ];

    $(selectedElem).each((parentIndex, parentElem) => {
      let keyIndex = 0;
      const coinDetails = {};
      if (parentIndex <= 9) {
        $(parentElem)
          .children()
          .each((childId, childElem) => {
            const value = $(childElem).text();
            if (value) {
              coinDetails[keys[keyIndex]] = value;

              keyIndex++;
            }
          });
        coinArray.push(coinDetails);
      }
    });
  });
  return coinArray;
}

从显示的代码来看,你已经将copy selector 字符串存储在selectedElem 变量中,并使用Cheerio的each 方法循环浏览这些行。每个方法都把parentIndexparentElement 作为参数。接下来,设置一个条件来选择前十行,并使用.children() 方法来循环浏览每一列和.text() 来获取数值。

这应该会给出诸如序列号、硬币名称、价格、24小时以及页面上显示的其他细节。一个名为coinDetails 的空对象被创建,以保存被搜刮的数据的键值对。

同时,为了将数据分配给标签,创建了一个名为keys 的数组,里面有标签,并且在每次循环运行子元素时,都会增加一个keyIndex 的计数器。这有助于将每个标签映射到其各自的子值。每个coinDetails ,使用push() 方法添加到coinArray

快递路线

你的搜刮器的最终代码应该类似于这样,编辑你的index.js文件。

const PORT = 5000;
const axios = require("axios");
const cheerio = require("cheerio");
const express = require("express");
const app = express();

async function cryptopriceScraper() {
  const url = "https://coinmarketcap.com/";
  const coinArray = [];
  await axios(url).then((response) => {
    const html_data = response.data;
    const $ = cheerio.load(html_data);

    const selectedElem =
      "#__next > div > div.main-content > div.sc-57oli2-0.comDeo.cmc-body-wrapper > div > div:nth-child(1) > div.h7vnx2-1.bFzXgL > table > tbody > tr";
    const keys = [
      "No.",
      "Coin",
      "Price",
      "24h",
      "7d",
      "Marketcap",
      "Volume",
      "CirculatingSupply",
    ];

    $(selectedElem).each((parentIndex, parentElem) => {
      let keyIndex = 0;
      const coinDetails = {};
      if (parentIndex <= 9) {
        $(parentElem)
          .children()
          .each((childId, childElem) => {
            const value = $(childElem).text();
            if (value) {
              coinDetails[keys[keyIndex]] = value;
              keyIndex++;
            }
          });
        coinArray.push(coinDetails);
      }
    });
  });
  return coinArray;
}

app.get("/api/crypto", async (req, res) => {
  try {
    const crypto = await cryptopriceScraper();
    return res.status(200).json({
      result: crypto,
    });
  } catch (err) {
    return res.status(500).json({
      err: err.toString(),
    });
  }
});

app.listen(PORT, () =>
  console.log(`The server is active and running on port ${PORT}`)
);

作为最后一个过程,上面的代码设置了一个Express路由/api/crypto ,当它被调用时,将搜刮的数据发送到客户端。Express使用get ,该方法将请求和响应作为参数。

它实现了一个try-catch块来调用cryptoPriceScraper ,并在请求成功时在浏览器上显示一个JSON API,否则会显示错误信息。要查看搜刮的数据,请到你的浏览器中输入http://localhost:5000/api/crypto 。结果应该是下面的图片。

cryptodata

结论

在这个项目中,你已经学会了如何从一个加密货币网站刮取数据。你也已经熟悉了用Cheerio解析HTML元素以及操作的方法。有了这些知识,你可以通过你选择的任何网站进行搜刮,但要注意的是,在搜刮一个网站之前,首先要检查法律政策。