如何在Node.js和Cheerio抓取网页数据
在本文中,您将学习如何使用Cheerio和 NodeJS 进行网络抓取。Web 抓取是从网站中提取数据以用于特定用例和分析。
本教程将带您了解如何使用 Cheerio 从示例网站抓取 Pokémon 数据,然后根据数据创建 JSON 文件。在这里,我们的目的是教你如何在 NodeJS 中使用 Cheerio 来抓取任何类型的网页,使用 Axios 作为 HTTP 请求客户端。
什么是 Cheerio?
Cheerio 是专为服务器设计的核心 jQuery 的快速、灵活和精益实现。Cheerio 是一个基于htmlparser2构建的 JavaScript 库,具有与专为服务器端 DOM 操作而设计的 jQuery 类似的实现。它还为解析和遍历标记数据提供了强大的 API。
如果您不熟悉使用 JavaScript 和 Node JS 进行网络抓取,那么使用 JavaScript 和 Node.js 进行网络抓取的文章介绍是一个很好的起点。如果您不熟悉 jQuery 语法,一个很好的网页抓取 Cheerio 替代品是Puppeteer。
先决条件
在开始本文之前,您需要对使用 JavaScript 和 Node.js 编写代码有很好的了解。另外,在继续阅读本文之前,请确保您的计算机上安装了以下软件:
- 节点.js
- npm
- 代码编辑器——例如 VS Code 或 Atom
如果您不确定您的计算机上是否安装了 Node.js 或 npm,您可以分别通过在终端中运行node -v
和来确认npm -v
。
Cheerio 示例用法
在这里,我们将演示 Cheerio 在网络抓取中的基本用法,以帮助您了解该工具的工作原理。要开始抓取 Web 数据,您需要传递标记数据供 Cheerio 加载以构建 DOM。这通常是使用load
接受一串标记数据的函数来完成的。
这是加载 HTML 数据以进行抓取的推荐方式。在加载标记并初始化 Cheerio 之后,您可以开始使用 cheerio 的 API 操作和遍历生成的数据结构。
一个例子如下所示:
const cheerio = require('cheerio'); const $ = cheerio.load('<h2 class="title">Hello world</h2>') // Load markup // Use a selector to grab the title class from the markup and change its text $('h2.title').text('Hello there!'); $('h2').addClass('welcome'); // Add a 'welcome' class to the markup console.log($.html()); // The html() method renders the document
它将以下内容记录到控制台:
<html><head></head><body><h2 class="title welcome">Hello there!</h2></body></html>
还值得注意的是,Cheerio 将自动在呈现的标记中包含<html>
、<head>
和<body>
元素,就像我们在浏览器上下文中一样(仅当它们不存在时)。但是,您可以通过将false
第三个参数添加到load
函数中来禁用此行为,如下所示:
// ... const $ = cheerio.load('<h2> class="title">Hello world</h2>', null, false) // ...
Cheerio 的selector
API
在本节中,我们将了解一些可用于遍历和操作标记数据的 Cheerio 选择器。如果您以前使用过, APIselector
实现与 jQuery 非常相似。
该函数具有以下结构:$(selector, [context], [root])
selector
– 这用于定位标记数据中的特定元素。它是遍历和操作标记数据的起点。它可以是字符串、DOM 元素、元素数组或 cheerio 对象。context
– 选修的。定义范围或从何处开始查找目标元素。它可以是字符串、DOM 元素、元素数组或 cheerio 对象。root
– 这通常是您要遍历或操作的标记字符串。
或者,您可以直接使用选择器 API 加载标记数据,如下所示:
const cheerio = require('cheerio'); const $ = cheerio.load('') // This loads the HTML data, selects the last list item and returns its text content. console.log($('li:last', 'ul', '<ul id="fruits"><li class="apple">Apple</li><li class="orange">Orange</li><li class="pear">Pear</li></ul>').text())
请注意,不建议将上述方法用于加载数据,因此您应该只在极少数情况下使用它。
例如,给定以下 HTML 标记:
<ul id="fruits"> <li class="apple">Apple</li> <li class="orange">Orange</li> <li class="pear">Pear</li> </ul>
我们可以使用类名来定位列表项,apple
如下所示:
const cheerio = require('cheerio'); const $ = cheerio.load('<ul id="fruits"><li class="apple">Apple</li><li class="orange">Orange</li><li class="pear">Pear</li></ul>', null, false) // Load markup // Target the list item with 'apple' class name, then return its text content console.log($('li.apple').text())
以下是我们可以使用的选择器的其他示例:
$('li:last')
– 返回最后一个列表项。$('li:even')
– 返回所有偶数<li>
元素。
几乎所有的 jQuery 选择器都是兼容的,因此您不会局限于在给定标记数据中可以“找到”的内容。您可以在此处参考完整的 jQuery 选择器列表。
使用正则表达式提取数据
也可以使用 JavaScript 的字符串方法使用正则表达式模式在 Cheerio 中搜索数据match()
。
例如,给定以下 HTML 标记,其中包含用户名列表:
<ul class='usernames'> <li>janedoe</li> <li>maxweber</li> <li>greengoblin</li> <li>maxweber34</li> <li>alpha123</li> <li>chrisjones</li> <li>amelia</li> <li>mrjohn34</li> <li>matjoe212</li> <li>eliza</li> <li>commando007</li> </ul>
我们可以使用正则表达式搜索并返回包含数字的用户名,如下所示:
const cheerio = require('cheerio'); const markup = '<ul class="usernames"><li>janedoe</li><li>maxweber</li><li>greengoblin</li<li>maxweber34</li><li>alpha123</li><li>chrisjones</li><li>amelia</li><li>mrjohn34</li><li>matjoe212</li><li>eliza</li><li>commando007</li></ul>' const $ = cheerio.load(markup); // Load markup and initialize Cheerio const usernames = $('.usernames li'); // Get all list items const usernamesWithDigits = []; usernames.each((index, el) => { const regex = /\d/; // Search for usernames that contain digits const hasNumber = $(el).text().match(regex); if (hasNumber !== null) { usernamesWithDigits.push(hasNumber.input) } }) console.log(usernamesWithDigits) // Log usernames that contain digits to the console.
上面的代码将以下内容记录到控制台:
如何使用 Cheerio 抓取网页
在本节中,您将学习如何从 ScrapeMe 中抓取 Pokémon 数据,然后将生成的数据转换为 JSON 文件。该网页如下图所示:
让我们开始吧。
第 1 步 – 创建工作目录
首先,您需要创建一个项目存储库。在您的目录中运行以下命令以创建项目目录并移入其中:
mkdir cheerio-web-scraping && cd cheerio-web-scraping
显然,您不必为项目命名cheerio-web-scraping
。您始终可以选择一个您觉得舒服的名字。
第 2 步 – 初始化节点项目
首先,确保你在项目目录下,然后运行以下命令来初始化一个节点项目:
npm init -y
上面的命令将创建一个带有package.json
配置文件的新项目。
请注意,使用该-y
标志将自动跳过交互式提示并配置项目。
第 3 步 – 安装项目依赖项
在这一部分中,我们将为我们的项目安装必要的依赖项。运行以下命令以安装软件包:
npm install cheerio axios
上面的命令将安装以下软件包:
第 4 步 – 检查目标网站
在编写任何用于网络抓取的代码之前,了解您要抓取的网站的结构和内容至关重要。它将成为您编写代码时的向导,这样您就不会浪费时间抓取无用的数据。每个现代浏览器都带有用于检查网站和应用程序的开发人员工具。
在本教程中,我们将使用 Chrome 的 DevTools 来检查我们的目标网站。我们想从我们目标网站上的每个 Pokémon 中抓取三样东西:
- 图片网址
- 姓名
- 价格
下图显示了网站是如何使用 Chrome 的 DevTools 构建的:
从上图中可以看出,<ul>
类名为 的元素products
包含所有 Pokémon 的列表。在下图中,我们还可以看到每个列表元素都有一个类名product
,包含我们要抓取的所有数据。
现在我们了解了我们的目标网站结构,我们可以开始编写一些代码了。让我们开始吧!
第 5 步 – 编写代码
下一步是index.js
在项目根目录中创建一个文件。打开index.js
文件并向其中添加以下代码:
const axios = require('axios'); const cheerio = require('cheerio'); const fs = require('fs'); const targetURL = 'https://scrapeme.live/shop/'; const getPokemons = ($) => { // Get all list items from the unodered list with a class name of 'products' const pokemons = $('.products li'); const pokemonData = []; // The 'each()' method loops over all pokemon list items pokemons.each((index, el) => { // Get the image, name, and price of each pokemon and create an object const pokemon = {} // Selector to get the image 'src' value of a pokemon pokemon.img = $(el).find('a > img').attr('src'); pokemon.name = $(el).find('h2').text(); // Selector to get the name of a pokemon pokemon.price = $(el).find('.amount').text(); // Selector to get the price of a pokemon pokemonData.push(pokemon) }) // Create a 'pokemon.json' file in the root directory with the scraped pokemonData fs.writeFile("pokemon.json", JSON.stringify(pokemonData, null, 2), (err) => { if (err) { console.error(err); return; } console.log("Data written to file successfully!"); }); } // axios function to fetch HTML Markup from target URL axios.get(targetURL).then((response) => { const body = response.data; const $ = cheerio.load(body); // Load HTML data and initialize cheerio getPokemons($) });
让我们了解上面的代码中发生了什么。
我们的爬虫代码包含一个主要功能scraper()
。在scraper
函数中,我们有另一个名为的函数getPokemons
。在其中,我们正在编写使用 Cheerio 的选择器搜索我们正在寻找的 Pokemon 数据的实际抓取代码。
首先,我们使用选择器从类名为(在本例中为元素)的元素中$('.products li')
获取所有项目。这将返回一个包含所有列表项的 Cheerio 对象。然后,我们使用 Cheerio 的方法迭代 Cheerio 对象并运行一个回调函数,该函数针对 Cheerio 对象中的每个列表项执行。<li>
.products
<ul>
each()
在回调中,我们有一些选择器正在通过标记搜索我们想要的口袋妖怪数据。让我们来看看它们:
该pokemon
对象保存每个 Pokémon 的数据,然后我们将其放入一个pokemonData
数组中。这个数组将包含所有被抓取的神奇宝贝。
下一步是从pokemonData
数组创建一个 JSON 文件。为此,我们使用fs.writeFile
它异步将数据写入文件。它带有一个回调函数,在其中,我们使用该JSON.stringify()
方法将数组转换pokemonData
为 JSON 字符串。
现在是最后一步。打开package.json
文件并向其中添加以下代码:
// ... "scripts": { "dev": "node index.js" }, // ...
现在,如果您npm run dev
在终端中运行,pokemon.json
应该会自动创建一个文件,并且消息“数据已成功写入文件! ”也应该会记录到控制台。
这是创建的pokemon.json
文件的样子:
您在 NodeJS 中使用 Cheerio 构建了一个网络抓取工具!现在已准备好处理 Web 上的每条数据!
结论
总之,您已经了解了如何在 NodeJS 中使用 Cheerio 和 Axios 抓取 Web 数据。我们向您介绍了网页抓取的过程、抓取网站之前需要了解的内容以及完成抓取的工具。
我们希望本文能帮助您开始构建自己的网络抓取工具,我们很高兴看到您接下来构建的内容!
您经常遇到的挑战是被网站阻止,因此我们建议您查看我们的Web Scraping Without Getting Blocked指南。