使用JavaScript和Puppeteer进行网络刮削的介绍

407 阅读4分钟

在靠近用户的地方部署容器

本工程教育(EngEd)计划由科支持。

在全球范围内即时部署容器。Section是经济实惠、简单而强大的。

免费入门

使用JavaScript和Puppeteer进行网络刮削的介绍

9月8日, 2021

我们的大多数网络应用都需要数据来处理并呈现给用户。数据的来源有很多,比如数据库和API。

但我们也可以从任何网站上获取数据,即使它没有提供公共API。这个过程被称为网络刮削,我们将在本文中对其进行考察。

我们将使用Puppeteer和Node.js(一种JavaScript运行时环境)。关于puppeteer的更多信息可以在这里找到。

目标

在本教程结束时,你应该能够从任何网站获取数据并在网页上显示。

前提条件

为了能够继续学习本教程,你需要对以下概念有所了解。

  • HTML和CSS的基本知识。
  • 中级的JavaScript知识。
  • 一个代码编辑器,最好是VS Code,安装在你的机器上。
  • 一个网络浏览器,最好是chrome。
  • 在你的电脑上安装Node.js。

你应该注意,不是所有的网站都允许搜刮数据。在你从一个网站上搜刮数据之前,请确保检查该网站的政策。在这篇文章中,我们将搜刮亚马逊。只要你提取公开可用的数据,如产品信息、价格和评论,亚马逊就允许搜刮。

设置我们的项目

打开你的代码编辑器,创建一个名为price-tracker的文件夹。然后,打开你的终端并输入。

npm init -y

上面的代码将创建一个JSON文件,并将其存储在我们的目录中。

接下来,我们需要安装各种npm包。

  • express - Express是Node.js框架,我们要用它来配置我们的后端。
  • puppeteer - 我们将用它来访问网页并提取我们需要的数据。
  • nodemon - Nodemon在保存文件后检测到变化时自动重新启动node应用程序。

要安装这些npm包,请在终端运行以下命令。

npm install express
npm install --save-dev nodemon
npm install puppeteer

当你安装puppeteer时,它会自动下载最近/更新的chromium版本。

设置我们的服务器

为了用Express启动我们的Node.js服务器,我们创建了一个名为index.js 的文件,作为入口点。

打开该文件并添加以下代码。

const express = require("express");
const app = express();
const port = 3000;

app.get("/", (req, res) => {
  res.send("Server is running");
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

这是用于创建服务器的Express JS启动器模板。

我们首先使用const express = require('express') ,导入Express包。

然后,我们使用const app = express() 创建一个我们的应用程序的实例,并使用const port = 3000 指定我们要监听的端口。

app.listen 是一个需要两个参数的方法。第一个参数是服务器要监听的端口号。第二个参数是一个回调函数,当连接成功建立时将被执行。

要启动服务器,键入以下命令并按回车键。

nodemon index.js

现在,当你导航到http://localhost:3000/ ,你会看到 "服务器正在运行 "的信息。这意味着你已经成功地启动了服务器,现在你可以继续进行下一步。

设置puppeteer

既然我们的服务器在工作,现在是时候设置 puppeteer,开始进行网络刮削了。在我们的index.js文件中,在设置端口号的那一行下面添加以下代码。

const puppeteer = require("puppeteer");

上面的代码将puppeteer包导入我们的应用程序中。

在本教程中,这是我们将在亚马逊上跟踪的产品的URL

因此,我们使用下面这行代码将URL分配给一个常量。

const url = 'https://www.amazon.com/Redragon-S101-Keyboard-Ergonomic-Programmable/dp/B00NLZUM36/'

现在是配置浏览器的时候了。在index.js文件中加入这段代码。

async function configureTheBrowser() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(url, { waitUntil: "load", timeout: 0 });
  return page;
}

该代码解释如下。

  • puppeteer.launch() - 它是一个启动浏览器的puppeteer方法。
  • puppeteer.newPage - 它是一个puppeteer方法,用于打开一个新页面。
  • puppeteer.goto() - 它是一个指示浏览器前往哪个URL的方法。它需要两个参数,尽管第二个参数是可选的。第一个参数是实际的URL。第二个参数用于在你正在加载的页面很重的情况下禁止超时错误。

接下来,我们需要抓取页面标记并提取我们需要的确切信息。

在这样做之前,打开你的浏览器,进入提供的URL。从这个页面,我们需要获得键盘的图像、价格和名称。遵循这些步骤。

  1. 右键点击图片,选择检查选项。
  2. 上面的操作将打开开发者工具。在元素标签上,你会看到页面的标记,特别是img,因为它是我们要检查的。从图片的属性中注意到图片的id,并把它记在某个地方。在这个例子中,这个idlandingImage
  3. 接下来,右键点击键盘的价格,选择检查。记下包含键盘价格的跨度的id。在这个例子中,id是priceblock_ourprice
  4. 在键盘名称上点击右键并点击检查。记下包含键盘名称的跨度的id。在这种情况下,它是productTitle

有了上述信息,我们现在可以编写我们需要的函数来提取我们需要的信息。下面是代码。

async function checkDetails(page) {
  let html = await page.evaluate(() => {
    return {
      product: document.querySelector("#productTitle").innerText,
      price: document.querySelector("#priceblock_ourprice").innerText,
      image: document.querySelector("#landingImage").src,
    };
  });
  return html;
}

代码解释。

  • 我们正在使用evaluate() 方法,这是一个用于获取网页内容的puppeteer方法。这个方法需要一个回调函数,在这个回调函数中,我们可以添加所需的代码来获取我们所需要的页面元素。在这种情况下,我们需要产品的名称、价格和它的图片SRC。这个回调函数返回一个包含我们从网页上获得的信息的对象。
  • 为了获得产品名称、价格和图片src,我们使用querySelector() ,该方法通常返回当前文档中与我们指定的选择器相匹配的第一个元素。要了解更多关于querySelector() ,请点击这里
  • 我们将evaluate 方法的结果分配给一个名为html 的变量。
  • 然后,checkDetails 函数返回html ,它是一个变量,包含我们从网页上抓取的信息。

设置快速路由

我们需要设置一个快速路由,一旦调用特定的路由,它将获得被抓取的数据并将其发送到我们的客户端。

app.get("/price", async (req, res) => {
  let page = await configureTheBrowser();
  let results = await checkDetails(page);
  res.send(results);
});

代码解释。

上面的代码定义了一个简单的路由,它响应了一个GET请求。要了解更多关于快速路由的信息,请点击这里

get 方法需要两个参数。一个是路由,另一个是路由被调用时执行的回调函数。

回调函数需要两个参数,即来自客户端的请求和它发回的响应。在回调函数中,我们调用configureTheBrowser() 函数,并将其返回的值存储在一个名为page的变量中。

然后我们调用checkDetails() 函数,并将page变量传递给它。我们将函数返回的值存储在一个名为results的变量中,最后将其作为一个响应发送到客户端。

下面是index.js文件的全部代码。

const express = require("express");
const app = express();
const port = 3000;
const puppeteer = require("puppeteer");

//Serving static files
app.use(express.static("public"));

const url =
  "https://www.amazon.com/Redragon-S101-Keyboard-Ergonomic-Programmable/dp/B00NLZUM36/";

async function configureTheBrowser() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(url, { waitUntil: "load", timeout: 0 });
  return page;
}

async function checkDetails(page) {
  let html = await page.evaluate(() => {
    return {
      product: document.querySelector("#productTitle").innerText,
      price: document.querySelector("#priceblock_ourprice").innerText,
      image: document.querySelector("#landingImage").src,
    };
  });
  return html;
}

app.get("/price", async (req, res) => {
  let page = await configureTheBrowser();
  let results = await checkDetails(page);
  res.send(results);
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

设置我们的前端

在我们的项目目录中,创建一个名为public的文件夹。在该文件夹中,创建两个文件,名为index.htmlmain.js

打开index.html,粘贴下面的代码。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <style>
    .container {
      display: flex;
      flex-direction: row;
      padding: 1rem;
    }
    .details {
      display: flex;
      flex-direction: column;
      padding-left: 1rem;
    }
    img {
      width: 300px;
      height: auto;
    }
    span {
      padding: 1rem 0;
    }
  </style>
  <body>
    <div class="container">
      <div class="imageHolder">
        <img id="image" />
      </div>
      <div class="details">
        <span id="price"></span>
        <span id="product"></span>
      </div>
    </div>

    <button id="scrape">Check Price</button>

    <script src="main.js"></script>
  </body>
</html>

这段代码包含了CSS代码,用来设计我们的网页和HTML代码。在HTML代码里面,我们有一个按钮,我们将用它来从后台获取页面数据。注意,HTML文件与一个js文件相连。

打开Js文件,输入下面的代码。

const scrape = document.getElementById("scrape");

scrape.addEventListener("click", (e) => {
  e.preventDefault();
  console.log("clicked");
  getPrice();
});

async function getPrice() {
  const response = await fetch("/price");

  //returns a promise so we need to convert it json
  const data = await response.json();
  console.log(data);

  document.querySelector("#image").setAttribute("src", data.image);
  document.querySelector("#price").innerText = data.price;
  document.querySelector("#product").innerText = data.product;
}

这个Js文件监听按钮上的点击事件,并使用一个异步函数和fetch API一起从后端获取数据。

要了解更多关于fetch API的信息,请点击这里

开始我们的应用程序

保存你所有的文件并打开终端。输入以下命令并按回车键。

nodemon index.js

这将启动服务器。打开你的浏览器并导航到这个URL。监听示例应用程序:http://localhost:3000/

该页面包含一个文本为 "检查价格 "的按钮。点击它,等待键盘的细节显示。

结论

这个例子很简单,但它是对网络搜刮的一个很好的介绍。在你搜刮数据之前,一定要确保检查一个网站是否允许网络搜刮。现在我们已经成功地搜刮并跟踪了一个产品的价格,继续尝试跟踪多个产品。

你也可以创建一个网络应用,跟踪不同卖家的类似商品的价格,比较价格,并向用户建议他/她应该从哪个卖家购买。

刮痧快乐

进一步阅读


同行评审投稿人:。Dawe Daniel

类似文章

[

How to Create a Reusable React Form component Hero Image

语言

如何创建一个可重复使用的React表单组件

阅读更多

](www.section.io/engineering…

Building a payroll system with next.js Hero Image

语言, Node.js

用Next.js构建一个薪资系统

阅读更多

](www.section.io/engineering…

Creating and Utilizing Decorators in Django example image

架构

在Django中创建和使用装饰器

阅读更多

](www.section.io/engineering…)