如何使用 Puppeteer 在具有 Netlify 无服务器功能的 API 中自动化 Chrome
自动化通常包括完全基于代码的任务,甚至不考虑浏览器,但有些任务需要交互并使用浏览器,就像人类在网站上执行搜索一样。 我们如何利用可以自动化浏览器并将其打包到无服务器 API 端点以使其易于访问的工具?
里面是什么?
- 什么是木偶师?
- 我们如何在 Netlify 的无服务器功能中使用 Puppeteer?
- 我们要建造什么?
- 步骤 0:创建一个新的节点项目
- 步骤 1:安装和配置 Netlify CLI
- 第 2 步:创建新的无服务器功能
- 第 3 步:安装 Chrome 和 Puppeteer 以在无服务器功能中使用
- 第 4 步:使用 Puppeteer 设置新浏览器以获取页面标题和 SEO 元数据
- 第 5 步:使用网站搜索来查找内容
- 第 6 步:将函数部署到 Netlify
- 接下来你能做什么?
什么是木偶师?
Puppeteer 是 Google 的一个 JavaScript 库,它允许开发人员通过 API 控制 Chrome。
一个常见的用例是使用 Puppeteer 进行测试,我们可以确保我们在浏览器中执行的操作按预期工作。
但是我们也可以使用它来自动化我们想要在浏览器中以编程方式执行的任务。 例如,我们可以使用 Puppeteer 在 Chrome 中启动一个网站并获取它的灯塔分数( 参见 的示例 Puppeteer 团队 )。
我们如何在 Netlify 的无服务器功能中使用 Puppeteer?
Puppeteer 既可以打开可见的浏览器 UI,也可以“无头”地工作,这意味着它将作为一个进程运行,而无需实际启动 UI。
这使得它非常适合在您可能没有浏览器 UI 的地方运行,如 CI 环境以及您可能已经猜到的无服务器功能。
因此,我们可以利用此功能来构建可以使用 Puppeteer 执行操作的 API 端点。
我们要建造什么?
我们将使用 Netlify 创建一个新的无服务器函数,我们能够通过 API 端点向其发送请求。
在本文中,我们将学习如何利用 chrome-aws-lambda 和 Puppeteer 等包将所有这些打包到一个可以按需运行的无服务器函数中。
我们将使用 Netlify CLI 来处理我们的函数,但无论您如何运行和打包函数,它的工作方式都非常相似。
步骤 0:创建一个新的节点项目
对于这个项目,我们将从头开始,因为这不需要太多样板。
好消息是因为没有太多样板,这应该真正转移到任何项目,所以你应该在你现有的项目中没有问题。
首先,让我们为我们的项目创建一个新目录并导航到它:
mkdir my-puppeteer-function
cd my-puppeteer-function
复制代码
注意:随意为您的项目使用不同的名称!
然后我们将初始化一个新的节点项目,以便我们可以安装我们需要的包来提高自己的工作效率。
要创建一个新的节点项目,请运行:
初始化
复制代码
这将通过一系列问题询问您希望如何设置您的项目。 随意为所有这些输入回车并使用默认值,因为它们对于本演练并不重要。
提示:您可以随时更新这些值
package.json
!
在这一点上,我们现在有一个新的节点项目,我们可以开始使用我们的新项目来提高效率。
我还建议 将项目设置为 GitHub 存储库 。 这样做时,您要确保添加一个 .gitignore
根目录中的文件,包括您的 node_modules
避免犯那些。
为此,请创建一个 .gitignore
根目录中的文件,然后简单地添加:
节点模块
复制代码
现在我们应该准备好开始挖掘了!
步骤 1:安装和配置 Netlify CLI
正如我之前提到的,我们将使用 Netlify CLI 来管理我们的功能。 这将包括通过 npm 或 yarn 将 CLI 安装为全局包。 如果你想取消这条路线,你也可以尝试查看 netlify-lambda ,你可以将它安装为本地包,但它的工作方式可能会有所不同。
您可以 找到完整的 说明和文档 在 Netlify 上 ,但首先,我们要安装 CLI 包:
npm install netlify-cli -g
复制代码
安装后,您应该能够运行以下命令并查看可用选项列表:
网路化
复制代码
虽然仅此一项就可以让您开始使用 CLI,但我还建议您使用现有的 Netlify 帐户登录。
这将允许您在以后想要部署您的功能时更轻松地链接您的项目。
你可以通过运行来做到这一点:
netlify 登录
复制代码
Netlify 使这个过程变得超级简单,打开一个新的浏览器窗口,您可以在其中使用您的帐户进行授权,然后您将获得 CLI 的授权。
您也可以尝试运行以下命令:
netlify 开发者
复制代码
这应该启动一个本地服务器,但你会注意到它不会做任何事情,因为我们在项目中没有任何东西,这就是我们接下来要开始的地方!
第 2 步:创建新的无服务器功能
现在开始深入研究代码,我们想设置一个新的无服务器功能。
我们必须对此进行拆分:
- 函数本身包括文件和函数处理程序
- Netlify 配置文件(
netlify.toml
) 只允许我们指向我们想要创建函数的目录
从创建函数文件本身开始,让我们创建一个名为的新文件夹 functions
在我们项目的根目录和里面,添加一个名为 meta.js
(我们的第一个示例将从网页中获取一些元数据)。
注意:更喜欢与“functions”不同的目录名称随意使用其他名称,只需确保在演练的其余部分使用相同的名称。
里面 functions/meta.js
添加:
export.handler = 异步函数(事件,上下文){
返回 {
状态代码:200,
正文:JSON.stringify({
状态:'好的'
})
};
}
复制代码
这将创建一个新的异步函数,它将充当我们的“处理程序”,它基本上在我们到达端点时运行。
在里面,我们返回一个 200 状态代码,这意味着它是一个成功的请求,以及一个带有简单状态的主体,表示“OK”。
现在在我们可以使用它之前,我们需要创建我们的配置文件。
创建一个名为的新文件 .netlify.toml
在项目的根目录中。
里面 .netlify.toml
添加:
[建造]
函数 = "函数"
复制代码
这告诉 Netlify 我们要在名为“functions”的文件夹中创建我们的函数!
而现在,我们一直在等待的那一刻。
我们可以启动我们的开发服务器并查看这项工作!
运行以下命令:
netlify 开发者
复制代码
您应该在终端中看到几行,说明 CLI 找到了您的函数并在指定端口(默认为 8888)启动了服务器。
使用 Netlify CLI 启动本地开发服务器
Netlify 甚至会尝试在浏览器中打开它,但它不会找到任何东西,因为我们没有任何项目要显示。
但是,如果我们尝试访问 http://localhost:8888/.netlify/functions/meta ,我们应该会在浏览器中看到 JSON 响应!
成功请求无服务器功能端点
虽然这看起来并不多,但我们刚刚创建了一个新的 API 端点,我们可以在其中开始编写自定义代码!
第 3 步:安装 Chrome 和 Puppeteer 以在无服务器功能中使用
我们有了新的无服务器功能,我们可以看到它在浏览器中运行,现在我们需要安装运行 Chrome 和 Puppeteer 所需的工具。
我们将为此使用两个依赖项:
Psst:从技术上讲,我们将使用第三个,但稍后我们会看到原因!
我们的无服务器功能默认没有 Chrome 可用,我们也没有真正“安装”它的机制。 chrome-aws-lambda 打包了 Chromium 二进制文件,以便我们可以将它与我们项目的其他依赖项一起用作节点包。
puppeteer-core 是 Puppeteer 的驱动功能,但它与标准 的最大区别在于它 puppeteer 包 不附带浏览器。 因为我们需要通过 chrome-aws-lambda 提供我们自己的浏览器,我们不想尝试将额外的浏览器添加到我们的包中,因为我们在无服务器功能中受到文件大小的限制。
既然我们知道为什么要使用这些包,让我们安装它们。
纱线添加 chrome-aws-lambda puppeteer-core
# 或者
npm 安装 chrome-aws-lambda puppeteer-core
复制代码
一旦完成,我们就可以深入研究实际代码了!
第 4 步:使用 Puppeteer 设置新浏览器以获取页面标题和 SEO 元数据
首先,我们需要先导入我们的依赖项。
在顶部 functions/meta.js
添加:
constchrome = require('chrome-aws-lambda');
const puppeteer = require('puppeteer-core');
复制代码
接下来,Puppeteer 的工作方式是,我们通过将浏览器的实例与已安装的浏览器副本 (Chromium) 相关联并启动它来创建浏览器实例。
在处理程序函数的顶部添加以下内容:
const browser = await puppeteer.launch({
args: 铬.args,
可执行路径:等待chromium.executablePath,
无头:真的,
});
等待 browser.close();
复制代码
我们正在使用 Puppeteer 的 launch
从我们的 Chromium 实例传入标志的方法,一个可执行路径(浏览器应用程序从中启动),chromium 包能够找到和确定,以及无头标志设置为 true
因为我们不想尝试启动 UI。
注意最后,我们还使用了 close
方法。 我们希望确保我们始终清理浏览器以避免挂起请求和浪费资源。
现在,在我们更进一步之前,让我们确保一切正常。 当我们启动它时,我们不会看到任何实际“发生”的事情,因为它在无头运行,我们没有对它做任何事情,但我们也不希望看到任何错误
在您的终端中,运行:
netlify 开发者
复制代码
然后尝试在浏览器中打开该函数 http://localhost:8888/.netlify/functions/meta 。
哦哦,你会注意到我们实际上得到了一个错误!
不幸的是,当尝试在本地运行时,chrome-aws-lambda 并不能“正常工作”。 这 应该可以 如果您将其按原样部署到 Netlify, 工作,但如果我们在开发它时无法在本地测试它,它对我们没有多大好处。
好消息是,我们可以在本地运行时覆盖我们的可执行路径,通过使用环境变量来使用我们现有的 Chrome 安装!
注意:chrome-aws-lambda 有一个 解决方法可以在本地运行项目, 将 puppeteer 作为开发依赖项安装,我没有太多运气让它自己工作。
首先,我们将使用流行的 dotenv 包,它可以轻松设置。 在你的终端运行:
纱线添加 dotenv
# 或者
npm 安装 dotenv
复制代码
接下来,里面 functions/meta.js
,更新 executablePath
到:
可执行路径:process.env.CHROME_EXECUTABLE_PATH || 等待chromium.executablePath,
复制代码
这告诉 Puppeteer,我们首先要尝试查看是否设置了环境变量(本地),如果没有(生产环境),则尝试找到 Chromium 的路径。
现在我们需要设置该环境变量。
在项目的根目录中,创建一个名为 .env
并添加:
CHROME_EXECUTABLE_PATH="/path/to/chrome"
复制代码
现在可能是棘手的部分,找到这条路。
幸运的是,Chrome 使这变得有些容易。 如果我们 访问 chrome://version/ 在浏览器中 ,我们应该能够找到一个名为 Executable Path 的字段,这正是我们需要的!
这是我在 Mac 上的样子:
Chrome 的可执行路径
所以现在,我们可以将该值插入到我们的环境变量中:
CHROME_EXECUTABLE_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
复制代码
如果我们重新启动我们的开发服务器以便变量启动,我们现在应该能够在浏览器中刷新端点并再次看到我们的“Ok”状态!
注意:在我们继续之前,请确保还添加
.env
到您的 .gitignore 文件,因为我们不想将其推送到存储库。
现在使用我们的浏览器,我们可以通过创建一个新页面并导航到我们选择的网站来开始我们的 Puppeteer 交互。
在浏览器常量下面添加以下内容:
const page = await browser.newPage();
await page.goto('https://spacejelly.dev/');
复制代码
注意:随意将 URL 自定义为您想要的任何内容!
如果我们尝试运行它,我们仍然看不到任何事情发生。 让我们通过查找页面标题并将其与我们的数据一起返回来解决这个问题。
在我们导航到我们选择的网站后,添加:
const title = await page.title();
复制代码
在我们的 return 语句中,将以下内容作为新属性添加到 status
:
正文:JSON.stringify({
状态:'好的',
页: {
标题
}
})
复制代码
这将告诉 Puppeteer 获取页面的标题,然后在我们的响应中返回它。
现在,如果我们在浏览器中刷新页面,我们应该会看到我们的页面标题!
使用 Puppeteer 返回页面标题的元函数
我们甚至可以使用 Puppeteer API 随心所欲地扩展它。 例如,如果我们还想获取元描述,我们可以在标题下添加:
const description = await page.$eval('meta[name="description"]', element => element.content);
复制代码
注意:没有像标题那样的原生 API 来获取描述,所以我们需要找到标签并手动评估它
和以前一样,在我们的数据中返回它:
页: {
标题,
描述
}
复制代码
如果我们刷新浏览器,我们现在应该看到标题和描述!
端点响应中的元描述
第 5 步:使用网站搜索来查找内容
Puppeteer 很酷的一点是我们拥有大量的功能。 我们可以与页面进行交互,并且真的可以做很多真人会在网页上做的事情。
为了测试这一点,让我们尝试一个在 spacejelly.dev 上进行搜索并获取结果列表的示例。
我们将通过复制当前端点并创建一个新端点来开始处理。
在您的项目中,复制 functions/meta.js
到一个新文件 functions/results.js
.
文件的大部分外壳将是相同的,因为我们将创建一个新的浏览器,就像我们对元数据所做的一样,只是这一次,我们将搜索页面而不是获取标题和描述!
开始,里面 functions/results.js
将标题和描述行替换为:
await page.focus('#search-query')
等待 page.keyboard.type('api');
const results = await page.$$eval('#search-query + div a', (links) => {
返回 links.map(link => {
返回 {
文本:link.innerText,
href: 链接.href
}
});
});
复制代码
这将使浏览器专注于搜索输入,然后输入查询“api”,这将弹出搜索结果客户端。
一旦可用,我们就可以找到这些结果并评估它们,抓取链接内的文本和位置,将其存储在一个 results
多变的。
所以最后,让我们用我们的数据返回它。 在我们的 return 语句中,添加:
返回 {
状态代码:200,
正文:JSON.stringify({
状态:'好的',
结果
})
};
复制代码
现在随着我们的开发服务器运行,如果我们到达端点,我们应该看到我们的结果!
响应数据中的搜索结果
第 6 步:将函数部署到 Netlify
最后,我们希望在生产中看到这项工作,所以让我们将其部署到 Netlify。
因为我们使用的是 Netlify CLI,这实际上很容易从我们的终端中完成!
首先,运行:
netlify 部署
复制代码
它首先会询问您是要链接到现有项目还是创建一个新项目。 如果您正在跟进,您可能想要创建一个新的。 如果您在现有项目中,您可能希望遵循现有项目。
然后,您将选择您的 Netlify 帐户的团队和站点名称。 您还会被要求提供一个发布目录,如果您继续关注,您可以使用默认目录 .
,所以只需按 Enter。
将预览部署到 Netlify 时的日志
此时,Netlify 仅部署了一个预览版,您可以在网站草稿 URL 中查看该预览版。
要看到这一点,我们可以获取该 URL 并将路径附加到函数。 在上面屏幕截图中显示的示例中,它看起来像:
https://6127137f71ef564eb08211ac--my-puppeteer-function.netlify.app/.netlify/functions/meta
复制代码
这应该像在本地一样工作!
注意:我删除了我的部署,所以上面的链接实际上不起作用!
如果我们准备好了,我们可以使用以下命令将其部署到生产环境中:
netlify 部署 --prod
复制代码
一旦完成,我们现在可以看到我们使用 Puppeteer 和 Chrome 部署到 Netlify 的新无服务器功能!
接下来你能做什么?
摩尔木偶师
Puppeteer 库有很多值得尝试的地方。 如果您可以自己在浏览器中执行此操作,则很可能您可以找到一种使用 Puppeteer 执行此操作的方法。
这对于测试之类的事情来说非常有用,在这种情况下,您可能想确保网站的特定部分正在工作,并且您想通过端点来做到这一点。 或者,如果您想进行一些网络抓取以从网站获取实时数据。 (一定要讲道德!?)