如何使用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 应该得到下面的结果。
从上面的图片来看,你有权限从主页上刮取数据,但它不允许你刮取个别货币页面中的一些标签。
创建项目
对于这个项目,你将在你的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
终端上的显示应该是这样的。
棒极了!它起作用了。
注意:当你对你的脚本进行修改时,你不一定要输入
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'),还可以通过使用$('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 方法循环浏览这些行。每个方法都把parentIndex 和parentElement 作为参数。接下来,设置一个条件来选择前十行,并使用.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 。结果应该是下面的图片。
结论
在这个项目中,你已经学会了如何从一个加密货币网站刮取数据。你也已经熟悉了用Cheerio解析HTML元素以及操作的方法。有了这些知识,你可以通过你选择的任何网站进行搜刮,但要注意的是,在搜刮一个网站之前,首先要检查法律政策。