你在进行网页抓取时,是否曾需要处理 POST 请求?我相信你一定遇到过!而且大多数时候,我们需要处理的都是表单。正因如此,今天我将探讨如何使用 Puppeteer 提交表单。如果你还不了解 Puppeteer,别担心。 马上你就会知道。在此之前,让我先为今天的文章设定一些预期目标。如果你一直跟着我的学习路径,今天你应该能够学会:
- 在网页抓取中,Puppeteer是什么
- 如何搭建一个简单的 Puppeteer 项目
- Puppeteer 中如何处理表单提交
那么,废话不多说,让我们开始吧!

你在进行网页抓取时,是否曾需要处理 POST 请求?我相信你一定遇到过!而且大多数时候,我们需要处理的都是表单。正因如此,今天我将探讨如何使用 Puppeteer 提交表单。如果你还不了解 Puppeteer,别担心。 马上你就会知道。在此之前,让我先为今天的文章设定一些预期目标。如果你一直跟着我的学习路径,今天你应该能够学会:
那么,废话不多说,让我们开始吧!
通常来说,网页抓取是指从各种服务器中自动提取数据的过程。早些时候,一个简单的 HTTP 客户端就足以完成这项任务。但如今,网站越来越依赖 JavaScript。而传统的 HTTP 客户端无法渲染 JavaScript 文件。这就是 Puppeteer 发挥作用的地方。
Puppeteer 是一个 Node.js 库,允许您通过 DevTools 协议控制无头版 Chrome 或 Chromium 浏览器。简而言之,它提供了一个用于自动化控制 Chrome 的高级 API。
在网页抓取方面,Puppeteer 特别适用于抓取那些需要渲染 JavaScript 的网站。此外,它还能像人类一样与网页进行交互,例如点击按钮,或者我们今天要重点探讨的——填写表单。这使得它成为抓取采用反抓取技术的网站的理想工具。
我认为放慢节奏有助于更好地理解整体流程。在探讨如何使用 Puppeteer 提交表单之前,让我们先了解基础的 Puppeteer。在本节中,我将向您展示如何创建 Node 项目、安装 Puppeteer 以及使用它进行数据抓取。 那么,首先,让我们创建一个新文件夹,并在我们喜欢的 IDE 中打开它。我个人偏好 Visual Studio Code,但您也可以自由选择任何您喜欢的工具。
你知道吗?
如果你愿意,可以将这四条命令结合起来,像这样在几秒钟内启动一个项目:
~ » mkdir scraper && cd scraper && npm init -y && code .
在 IDE 中打开一个新终端(Terminal > New Terminal),然后安装 Puppeteer。在终端中输入 `npm i puppeteer --save`。另外,我更倾向于使用 JS 模块而非 CommonJS。您可以在此处查看两者的区别。如果您也想使用模块,请打开 `package.json` 文件,并在 JSON 对象中添加 `"type": "module"`。
现在一切就绪,我们可以开始编写代码了。创建一个新的 `index.js` 文件并在 IDE 中打开它。这次无需通过终端操作,但作为提示,你可以使用 `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
}让我们看看具体做了什么:
到目前为止,操作并不复杂。这是使用 Node.js 和 Puppeteer 实现网页抓取的最基本示例。若要运行代码,只需向 `scraper` 函数传入目标页面,并记录其返回值即可:
console.log(await scraper('https://webscrapingapi.com/'))
但请记住,我们的目标是在提交表单时提取数据。这意味着我们需要设法使用 Puppeteer 提交表单。幸运的是,我之前做过,知道这并不难。那么,让我们看看你如何也能做到。
不妨将 Puppeteer 视为在特定网站上模拟人类行为的工具。 我们人类是如何提交表单的呢?通常,我们会找到表单,填写内容,然后点击按钮。Puppeteer 提交表单的逻辑与此完全相同。唯一的区别在于执行这些操作的方式。因为人类依赖感官,而 Puppeteer 是软件,所以我们将通过编程方式,利用 Puppeteer 的内置方法来实现,如下所示:
首先,我们需要“可视化”表单。在网站中,所有元素都归类在 HTML 块中,且每个元素都有一个标识符。标识符通常由元素的 CSS 属性组成。不过,您可能会遇到没有此类选择器的网站。在这种情况下,您可以使用 xPath 等方法。但这属于另一个话题。让我们专注于使用 CSS 在 Puppeteer 中识别元素。
为了便于理解,假设我们要自动化执行 Stack Overflow 的登录操作。目标页面是 https://stackoverflow.com/users/login。打开浏览器,访问登录页面,并开启开发者工具。你可以右键点击页面并选择“检查”。此时你应该看到如下内容:
左侧是图形界面,右侧是 HTML 结构。仔细观察右侧,你会看到我们的表单。它主要由两个输入框和一个按钮组成。这三个元素就是我们的目标。如你所见,这三个元素都带有 `id` 作为 CSS 标识符。现在让我们将迄今所学的内容转化为代码:
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))有时,Web自动化需要我们上传文件,而非仅提交简单的表单。若遇到此类任务,且需要在通过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.$` 先定位元素。只有在此之后,我才调用 `uploadFile` 方法——该方法仅适用于 `ElementHandle` 类型。在参数方面,与之前一样,我使用一个对象将所有数据一次性传递给函数。若要测试脚本,只需添加以下代码,并在终端中运行 `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)
Mihnea-Octavian Manolache 是 WebScrapingAPI 的全栈及 DevOps 工程师,负责开发产品功能并维护确保平台平稳运行的基础设施。