使用 Puppeteer、11ty 和 Netlify 实现自动化社交图片分享
好的
了解如何利用11ty强大的灵活模板功能,在 Netlify 构建中使用 Puppeteer 生成模板,以自动生成社交分享屏幕截图。
现在,您可以将其作为软件包/插件使用,以便更轻松地将其添加到您的 Eleventy 网站或其他 SSG 中。
我的第一个11ty网站ModernCSS.dev上线后,我正准备在 Twitter 上分享它,这时我意识到它没有图片。
我深入研究了好几个方向,找到了一些资源,这些资源让我接近目标,但最终还是没能完全实现以下目标:
- 仅使用构建产物生成图像,不包含任何已发布的额外 HTML
- 必须在构建过程中完成,而不能依赖于外部 Netlify 函数。
Søren 的帖子概述了一个过程,让我非常接近如何生成模板,而Gersom 的帖子填补了空白,帮助 Puppeteer 和 Netlify 构建部分正常工作。
让我们来看看这个综合解决方案,它完全包含在构建过程中,没有任何额外的生产工件。
创建屏幕截图的HTML模板
11ty 的优势就在于此——我们可以使用现有的数据集合,并定义一个新的布局模板,该模板仅作为构建产物生成。
如果您已经根据数据生成动态模板,则可以在该位置创建一个新文件。
如果这是你的第一个数据模板,请创建它generate/social-images.njk。目录名称由你决定,“generate”在11ty中并非保留或特殊名称。它必须位于你定义的输入目录中,或者如果你没有更改输入,则位于项目根目录中。这样,11ty就能找到并使用它,而无需其他任何指向它的文件。
您还需要创建目录,用于存放我们的节点函数和相关的构建产物,包括编译后的模板。我选择functions在项目根目录下创建一个目录。
接下来,这里有一个 Nunjuck 模板的示例屏幕截图,您可以对其进行修改以适应您的设计:
---
permalink: functions/template.html
permalinkBypassOutputDir: true
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@400;500&family=Kanit:ital,wght@0,800;1,600&display=swap" rel="stylesheet">
{% set css %}
{% include "src/css/style.css" %}
{% endset %}
<style>
{{ css | safe }}
</style>
</head>
<body>
<header>
<h1></h1>
<p>MySite.com | Tagline</p>
</header>
</body>
关于永久链接的 frontmatter 部分会神奇地将编译后的 HTML 代码放入其中functions/template.html,而使用 `--build-file` 则permalinkBypassOutputDir: true确保这只是一个构建产物。如果您愿意,实际上可以使用 `--build-file` 将functions/template.html其排除.gitignore,因为它会在构建命令中生成。
还有几点重要说明:
- 您可以选择其他模板语言,但它应该能够渲染出一个有效的完整 HTML 文档。
- 请务必包含视口元标签,以便调整大小功能能够正确处理所有样式,例如流式排版。
- 将你使用的任何 CSS 代码内联——示例展示了如何做到这一点。
- 创建占位符文本元素,我们将在 Puppeteer 函数中填充这些元素,这里我们只有一个占位符文本元素。
h1
生成 JSON 格式的图像数据
Gersom 的帖子实际上是关于使用 GraphQL 数据的,因为他的解决方案是为 Gatsby 设计的,所以它包含一个紧密的for循环,可以按需重复填充 HTML 模板。
我一直苦于找不到遍历帖子的方法,因为这只是一个构建过程,我不想生成单独的 HTML 文件来进行“访问”和截图。后来我想到一个巧妙的办法,就是利用 11ty 的模板功能生成一个 JSON 文件(最初是用作网站搜索的数据)。
与我们的 HTML 模板类似,我们生成此文件的目的仅在于将其作为构建产物,并将其放入我们的functions目录中:
---
permalink: functions/posts.json
permalinkBypassOutputDir: true
---
[{% for post in collections.posts %}
{
"title":"{{post.data.post.title | jsonTitle}}",
"slug":"{{post.data.post.title | slug}}}"
}{% if forloop.last == false %},{% endif %}
{% endfor %}]
在这个例子中,我们从一个名为“posts”的现有集合中获取数据。如果这部分内容让你感到困惑,请查阅关于集合的文档。
我们使用该slug过滤器创建一个便于文件名命名的字符串,我们将使用该字符串作为图像文件名。
由于数据是 JSON 格式,我创建了一个过滤器来正确格式化数据。
筛选:jsonTitle
两大主要功能:
- 在最后三个单词之间添加不间断空格,纯粹是为了美观,避免最后一行出现孤行。
- 转义所有
"找到的转义符,因为这些转义符会导致 JSON 数据失效,造成数据错误。
eleventyConfig.addFilter("jsonTitle", (str) => {
let title = str.replace(/((.*)\s(.*)\s(.*))$/g, "$2 $3 $4");
title = title.replace(/"(.*)"/g, '\\"$1\\"');
return title;
});
添加所需依赖项
为了使其在构建流程和本地环境中都能正常工作,您需要安装以下组件。dev v. prod 依赖项非常重要,因为它允许您chrome-aws-lambda根据环境上下文选择要使用的 Puppeteer 版本。
npm i chrome-aws-lambda puppeteer-core
npm i -D puppeteer
更新 Netlify 构建命令
正如Gersom所描述的那样,我们需要定义它,AWS_LAMBDA_FUNCTION_NAME因为 Netlify 的构建定义NODE_ENV=development导致chrome-aws-lambda选择了错误的 Puppeteer。
解决方法是通过文件定义构建命令netlify.toml,并在命令前添加变量:
[build]
command = "AWS_LAMBDA_FUNCTION_NAME=trickpuppeteer npm run build"
创建用于生成图像的节点函数
好了——我们的模板和数据都准备就绪了!
(如果您还没有运行 eleventy,请运行它以确保我们的模板目前为止都能按预期生成。)
再次感谢Gersom,但他的解决方案还有一些更新。
在目录中创建一个functions名为 `.htm`images.js或类似名称的文件。请确保不要修改.gitignore此文件。
首先,我们需要chorome-aws-lambda文件路径函数。
const chromium = require("chrome-aws-lambda");
const fs = require("fs");
const path = require("path");
接下来,我们将开始编写异步函数,其余的选项都将放在这个函数中。
(async () => {
// We'll be filling this in
})();
接下来,我们启动 Chromium/Puppeteer 并开始newPage()。
const browser = await chromium.puppeteer.launch({
args: chromium.args,
executablePath: await chromium.executablePath,
headless: chromium.headless,
});
const page = await browser.newPage();
然后,我们加载 HTML 模板和 POST 请求的 JSON 数据(预期它们与此函数位于同一目录下),并将初始 HTML 渲染到无头 Chromium 浏览器实例中。此外,我们还会检查所有资源是否准备就绪,以确定文档是否已完全渲染。
// Load html from template
const html = fs.readFileSync(path.resolve(__dirname, "./template.html")).toString();
// Get generated post json
const posts = require("./posts.json");
// Render HTML
// Wait for 0 network connections to ensure webfonts downloaded
await page.setContent(html, {
waitUntil: ["networkidle0"],
});
// Wait until the document is fully rendered
await page.evaluateHandle("document.fonts.ready");
然后,我们使用该setViewport函数来设置推荐的社交分享图像尺寸600x315,并考虑到视网膜显示屏,使用设备缩放功能将其在技术上加倍。
// Set the viewport to your preferred image size
await page.setViewport({
width: 600,
height: 315,
deviceScaleFactor: 2,
});
然后我们在生产输出目录中创建一个目录(默认情况下是_site,我已将其自定义为public)。
经过反复试验,我们发现这部分内容必须包含在输出中,因为我们需要在 eleventy 创建帖子之后运行此流程,以便获取最新的 JSON 数据以及可能的 CSS。所以,我们不能依赖 eleventy 来移动生成的图片。
// Create an img directory in the output folder
const dir = path.resolve(__dirname, "../public/img");
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
现在我们遍历帖子数据,并用它来更新 HTML 模板,之后就可以截取屏幕截图并将图像直接保存到之前创建的输出目录中。
// Go over all the posts
for (const post of posts) {
// Update the H1 element with the post title
await page.evaluate((post) => {
// We use `innerHTML` since we added ` `
const title = document.querySelector("h1");
title.innerHTML = post.title;
// If you have other data to insert,
// find the DOM elements and update that here
}, post);
// Optional just for progress feedback
console.log(`Image: ${post.slug}.png`);
// Save a screenshot to public/img/slug-of-post.png
await page.screenshot({
path: `${dir}/${post.slug}.png`,
type: "png",
clip: { x: 0, y: 0, width: 600, height: 315 },
});
}
最后,我们关闭无头浏览器实例——确保这发生在for循环之外。
await browser.close();
在文章模板中添加社交分享标签
现在你应该能够成功生成图像,但我们需要在相应的模板中添加特定的元标签才能真正使用它们。
准备使用绝对 URL
对于社交网络抓取工具来说,相对 URL 不起作用,因此您可能需要定义一个本地.env文件和关联的模块导出,以使用环境变量,URL该环境变量作为 Netlify 构建环境变量提供,并且将是生产站点的基本 URL,例如https://example.com。
对于本地设置,首先npm i dotenv --save-dev如果您还没有使用.env。
然后在.env项目根目录下定义URL=http://localhost:808011ty 的默认值。
然后,您可以创建一个全局数据文件,该文件需要位于_data/您的输入目录中,例如site.js使用以下代码:
module.exports = {
url: process.env.URL,
};
您可以在模板文件中访问它site.url,部署到 Netlify 后,它将更新为您的生产 URL。
添加og和 Twitter 卡片标签
一般情况下,以下内容足以用于在 Twitter 和 Facebook 上分享。其他网站也往往会查找这些内容,但我没有花太多时间研究如何处理,所以结果可能因网站而异!
在您可能已有的内容模板中,将以下内容添加到 HTML 代码中<head>——示例使用的是 Nunjuck 模板。这些是必需的最低标签。
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ post.title }}">
<meta name="twitter:description" content="{{ post.description }}">
<meta property="og:image" content="{{ site.url }}/img/{{ post.title | slug }}.png" />
<meta name="twitter:image" content="{{ site.url }}/img/{{ post.title | slug }}.png" />
注意前面讨论过的用法site.url,以及使用slug过滤器来post.title创建图像文件名。
重要提示:部署到 Netlify 后,请记住,即使是在功能分支部署中,图像 URL 也会解析到您的生产域,因此要在功能部署中进行测试,您可能需要手动更改 URL 以测试它们是否存在。
添加到构建脚本
别忘了把这段代码添加到你的构建脚本中!
一种方法如下:
"scripts": {
"screenshot": "node functions/images.js",
"build": "eleventy ; npm run screenshot",
}
如果你有单独的开发脚本,也把它添加到那里。
在社交分享工具上进行验证
请访问以下页面,确保您的元标签和图片都能协同工作:
- Twitter 卡片验证器
- Facebook 分享调试器- 您可能需要选择“再次抓取”来刷新检索到的数据。
欢迎提出反馈意见!
您是否创建过类似的流程,或者知道可以改进的地方?请留言!
文章来源:https://dev.to/5t3ph/automated-social-sharing-images-with-puppeteer-11ty-and-netlify-22ln