返回博客
指南
米赫内亚-奥克塔维安·马诺拉切2023年2月28日阅读时间 7 分钟

如何使用 Puppeteer 创建刮板并提交表单

如何使用 Puppeteer 创建刮板并提交表单

什么是 Puppeteer,为什么它对网络搜索很重要?

一般来说,网络搜刮指的是从各种服务器中自动提取数据的过程。在过去,一个简单的 HTTP 客户端就足以完成这项任务。但如今,网站越来越依赖 JavaScript。而传统的 HTTP 客户端无法呈现 JavaScript 文件。这就是 Puppeteer 发挥作用的地方。

Puppeteer 是一个 Node.js 库,可让您通过 DevTools 协议控制无头 Chrome 或 Chromium 浏览器。简而言之,它为 Chrome 浏览器的自动化提供了一个高级 API。

在网络搜刮方面,Puppeteer 可用于搜刮需要 JavaScript 渲染的网站。此外,它还可用于以类似人类的方式与网页进行交互。例如,点击按钮或填写表格。这使得它非常适合于使用反抓取技术的网站。

设置简单的 Puppeteer 项目

我相信,慢慢来才能更好地理解整个过程。在介绍如何使用 Puppeteer 提交表单之前,我们先来谈谈简单的 Puppeteer。在本节中,我将向你展示如何建立一个 Node 项目、安装 Puppeteer 并用它来刮取数据。所以,首先,让我们创建一个新文件夹,并在我们想要的集成开发环境中打开它。我更喜欢 Visual Studio Code,但你也可以随意使用。

你知道吗?

  • 你可以通过输入 `mkdir` 命令,在终端 "编程 "创建一个新文件夹。
  • 您可以使用 `npm init -y` 命令来设置节点项目并接受默认值
  • 您可以使用 `touch` 命令创建新文件。
  • 您还可以使用 `code .` 命令打开 VSCode。

如果你愿意,可以将这四种方法结合起来,像这样几秒钟就能完成一个项目:

~ " mkdir scraper && cd scraper && npm init -y && code . 

在 IDE 中打开一个新终端(Terminal > New Terminal),然后我们来安装 Puppeteer。在终端中输入 `npm i puppeteer --save`。另外,我更倾向于使用 JS 模块而非 CommonJS。你可以在此处查看两者的区别。如果你也想使用模块,请打开 `package.json` 文件,并在 JSON 对象中添加 `"type": "module"`。

VS Code 显示一个 package.json 文件,以及通过 npm 安装 Puppeteer 的终端命令

现在我们已经准备就绪,可以开始添加代码了。创建一个新的 `index.js` 文件并在集成开发环境中打开它。这次不需要在终端上打开,但作为提示,你可以使用 `touch` 命令。 现在让我们添加代码:

import puppeteer, { executablePath } from 'puppeteer'

const scraper = async (url) => {

   const browser = await puppeteer.launch({

       headless: false,

       executablePath: executablePath(),

   })

   const page = await browser.newPage()

   await page.goto(url)

   const html = await page.content()

   await browser.close()

   return html

}

让我们看看我们在做什么:

  • 我们将 Puppeteer 和 `executablePath` 导入我们的项目
  • 我们要定义一个新函数,它接受一个 `url` 参数
  • 我们将使用 `puppeteer.launch` 启动一个新的浏览器 a. 我们指定以全功能模式运行 b. 我们使用 `executablePath` 获取 Chrome 的路径
  • 我们将打开一个新页面,并导航至 `url
  • 我们把 `page.content()` 保存在一个常量中
  • 我们关闭了浏览器实例
  • 最后,我们将返回刚刚获取的页面的 `html` 输出结果

到目前为止,事情并不复杂。这是用 Node JS 和 Puppeteer 实现网络刮刀的最低限度。如果要运行代码,只需给 `scraper` 函数一个目标并记录其返回值即可:

console.log(await scraper('https://webscrapingapi.com/'))

但请记住,我们的目标是在提交表单时提取数据。这意味着我们必须想办法用 Puppeteer 提交表单。幸运的是,我以前做过,我知道这并不难。所以,让我们看看你也能做到这一点。

如何使用 Puppeteer 提交表格

将 Puppeteer 视为在特定网站上模仿人类行为的手段。我们人类如何提交表单?我们识别表单,填写表格,然后点击按钮。这与 Puppeteer 提交表单的逻辑相同。唯一不同的是我们如何执行这些操作。因为人类依靠的是感官。由于 puppeteer 是一款软件,我们将使用 puppeteer 的内置方法以编程方式完成这些操作,就像这样:

#1:使用 Puppeteer 提交简单表格

首先,我们需要将表单 "可视化"。在网站中,所有元素都集中在一个 HTML 块中,每个元素都有一个标识符。标识符通常由元素的 CSS 属性组成。但是,您可能会遇到没有这种选择器的网站。在这种情况下,你可以使用 xPaths。但这是另一个话题。让我们专注于在 Puppeteer 中使用 CSS 识别元素。

假设我们想在 Stack Overflow 上自动执行登录操作,请先了解一下背景情况。因此目标是https://stackoverflow.com/users/login。打开浏览器,导航到登录页面,然后打开 "开发工具"。你可以右键单击页面并选择 "检查"。你应该会看到类似这样的内容:

位于 Chrome 开发者工具旁边的 Stack Overflow 登录表单,该工具正在为表单字段高亮显示 HTML 元素

左侧是图形界面。右侧是 HTML 结构。仔细观察右侧,你会看到我们的表单。它主要由两个输入和一个按钮组成。这就是我们的三个目标元素。正如你所看到的,这三个元素的 CSS 标识都是 "id"。让我们把目前学到的内容转化为代码:

import puppeteer, { executablePath } from 'puppeteer'

const scraper = async (target) => {

   const browser = await puppeteer.launch({

       headless: false,

       executablePath: executablePath(),

   })

   const page = await browser.newPage()

   await page.goto(target.url,{waitUntil: 'networkidle0'})

   await page.type(target.username.selector, target.username.value)

   await page.type(target.password.selector, target.password.value)

   await page.click(target.buttonSelector)

   const html = await page.content()

   await browser.close()

   return html

}

为了保持其功能性和可重用性,我选择用一个对象取代函数参数。这个对象包括目标 URL、输入选择器和值,以及提交按钮的选择器。因此,要运行代码,只需创建一个新的 `TARGET` 对象来保存数据,并将其传递给您的 `scraper` 函数:

const TARGET = {

   url: 'https://stackoverflow.com/users/login',

   username: {

       selector: 'input[id=email]',

       value: '<YOUR_USERNAME>'

   },

   password: {

       selector: 'input[id=password]',

       value: '<YOUR_PASSWORD>'

   },

   buttonSelector: 'button[id=submit-button]'

}

console.log(await scraper(TARGET))

#2:使用 Puppeteer 上传文件

有时,网络自动化要求我们上传文件,而不是提交简单的表单。如果你遇到这样的任务,并需要在使用 Puppeteer 提交表单之前附加文件,你会希望使用 Puppeteer 的`uploadFile` 方法。为了保持简单,我建议你为这个操作创建一个新函数:

const upload = async (target) => {

   const browser = await puppeteer.launch({

       headless: false,

       executablePath: executablePath(),

   })

   const page = await browser.newPage()

   await page.goto(target.url,{waitUntil: 'networkidle0'})

   const upload = await page.$(target.form.file)

   await upload.uploadFile(target.file);

   await page.click(target.form.submit)

  

   await browser.close()

}

看看这次我是如何使用 `page.$` 来首先识别元素的。然后,我才调用只适用于 `ElementHandle` 类型的 `uploadFile` 方法。在参数方面,和之前一样,我使用一个对象将所有数据一次性传递给我的函数。如果你想测试脚本,只需添加以下代码并在终端运行 `node index.js`:

const TARGET = {

   url: 'https://ps.uci.edu/~franklin/doc/file_upload.html',

   form: {

       file: 'input[type=file]',

       submit: 'input[type=submit]'

   } ,

   file: './package.json'

}

upload(TARGET)

结论

综上所述,我认为使用 Puppeteer 提交表单非常简单。此外,我还发现,与其他产品相比,Puppeteer 能完全处理这一操作。基本上,用户要做的就是正确识别元素。

这里需要说明的是,实际应用中的爬虫要想高效运行,还需要更多条件。大多数情况下,如果你在短时间内提交过多表单而“滥用”服务器,很可能会被封禁。因此,如果你想实现表单提交的自动化,我建议使用专业的爬虫服务。 在 Web Scraping API,我们确实提供了发送 POST 和 PUT 请求的选项。您可以在我们的文档中了解更多相关信息。

关于作者
Mihnea-Octavian Manolache,全栈开发工程师 @ WebScrapingAPI
米赫内亚-奥克塔维安-马诺拉什全栈开发工程师

Mihnea-Octavian Manolache 是 WebScrapingAPI 的全栈及 DevOps 工程师,负责开发产品功能并维护确保平台平稳运行的基础设施。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网页数据。