你有没有注意到,比如 Whatsapp 或 Telegram 这样的消息应用,是如何让你预览你发送的链接的?
WhatsApp和Telegram的链接预览
在这次帖子中,我们将使用Deno构建一个抓取API,该API接受一个URL并检索其元数据等信息,这样我们就可以从几乎任何网站获取标题、描述、图片等字段。
比如说:
试试这个命令来获取dev.to的元标签数据吧:
curl https://metatags.deno.dev/api/meta?url=https://dev.to
点全屏,取消全屏
会得到这个结果
{
"last-updated": "2024-10-15 15:10:02 UTC",
"user-signed-in": "用户已登录",
"head-cached-at": "1719685934",
"environment": "生产",
"description": "一个建设性和包容性的软件开发者的社交网络。每一步我们都会陪伴您。",
"keywords": "软件开发, 工程, rails, javascript, ruby",
"og:type": "website",
"og:url": "https://dev.to/",
"og:title": "DEV 社区",
"og:image": "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8lvvnvil0m75nw7yi6iz.jpg",
"og:description": "一个建设性和包容性的软件开发者的社交网络。每一步我们都会陪伴您。",
"og:site_name": "DEV 社区",
"twitter:site": "@thepracticaldev",
"twitter:title": "DEV 社区",
"twitter:description": "一个建设性和包容性的软件开发者的社交网络。每一步我们都会陪伴您。",
"twitter:image:src": "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8lvvnvil0m75nw7yi6iz.jpg",
"twitter:card": "summary_large_image",
"viewport": "视口",
"apple-mobile-web-app-title": "苹果移动网页应用标题",
"application-name": "应用名称",
"theme-color": "主题颜色",
"forem:name": "名称",
"forem:logo": "https://media.dev.to/cdn-cgi/image/width=512,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png",
"forem:domain": "域名",
"title": "DEV 社区"
}
点击全屏 从全屏退出
真的很酷对吧?
Meta 标签是什么以及为什么我们需要它们元标签是HTML中的元素,用于提供更多关于页面的信息给搜索引擎和其他访客。
通常,这些标签会包括一个名称或属性来定义这种信息的类型,以及包含该信息值的内容。这里有两个元标签的例子:
<meta name="description" content="HTML中的<meta>元素表示无法通过其他HTML元数据相关元素(如<base>、<link>、<script>、<style>或<title>)表示的元数据。">
<meta property="og:image" content="https://developer.mozilla.org/mdn-social-share.cd6c4a5a.png">
进入全屏模式,退出全屏模式
第一个标签是提供了页面的描述,而第二个标签是Open Graph标签(OG标签),定义了在社交媒体上分享时显示的图片。
一个实用的例子是用元标签来建立书签管理系统,这样就不用手动输入每个书签的标题、描述和图片,可以自动从书签网址抓取这些元信息。
开放图协议开放图谱是一个互联网协议,最初由 Facebook 创建,旨在标准化网页元数据的使用,以表示页面内容,它有助于社交网络生成丰富的链接预览。
更多详情请参阅 这里。
- Deno 采用安全的默认设置,这意味着它需要显式的权限来访问文件、网络和环境,减少了安全漏洞的风险。
- Deno 基于 web 标准构建,使用 ES 模块,并且旨在使用 Web 平台 API(如 fetch)而不是特定的 API,使得 Deno 代码看起来很像你在浏览器中编写的代码——但仍然有一些规范差异与浏览器。
- Deno 内置了对 TypeScript 的支持,允许你编写 TypeScript 代码而无需构建步骤。
- Deno 配备了一个标准库,包含了用于常见任务(如 HTTP 服务器、文件系统操作等)的模块。
- Deno 提供了一个代码检查器、格式化工具和测试运行器,让你可以使用该平台,而无需依赖第三方包或工具,使其成为全能的工具。
- Deno 提供了 Deno Deploy,这是一个用于无服务器 JavaScript/TypeScript 应用程序的可扩展平台,这些应用程序在全球分布,确保最小的延迟和最高的正常运行时间。
我们正在构建的API将包含两个部分,一个用于抓取和解析元标签的函数,以及一个响应HTTP请求的API后端。
获取元标签信息让我们从登录Deno Deploy开始。
登录后点击“新建游乐场”按钮
这将给我们一个 hello world
的起点。
接下来,我们将添加一个名为 getMetaTags
的函数,该函数接受一个网址,使用 Fetch API 获取该网址的 HTML,然后将其传递给一个用于解析 HTML 的库 (deno-dom)。
要将 deno-dom
添加到我们的项目中,我们可以使用 jsr 包管理器。
import { DOMParser, Element } from "jsr:@b-fuze/deno-dom"; // 引入DOMParser和Element模块
进入全屏模式 退出全屏
我们现在将使用Fetch API来获取HTML文本。
const headers = new Headers(); // 创建一个新的Headers对象
headers.set("accept", "text/html,application/xhtml+xml,application/xml"); // 设置请求头的接受类型
const res = await fetch(url, { headers }); // 发起网络请求,并传入请求头
const html = await res.text(); // 从响应中获取文本内容
点击全屏进入全屏,点击退出退出全屏(点击全屏按钮进入全屏,点击退出按钮退出全屏)
拿到HTML后,我们可以使用deno-dom
来解析它,然后使用标准的DOM函数如querySelectorAll
来获取所有的meta
HTML元素,遍历它们,并用getAttribute
来获取每个标签的name、property和content。
const document = new DOMParser().parseFromString(html, "text/html");
const metaTags = document.querySelectorAll("meta");
const documentMeta = (Array.from(metaTags) as Element[])
.reduce((acc, meta) => {
const 属性 = meta.getAttribute("property");
...
进入全屏,切换回正常模式
我们将页面的 <title>
元素的内容查询出来,并把它作为字段添加到我们 API 中。
/* 若为null或undefined,则赋值 */
documentMeta.title ??= document.querySelector("title").textContent;
/* 从文档中选择"title"元素的文本内容 */
documentMeta.title ??= document.querySelector("title").textContent;
documentMeta.title = documentMeta.title ?? document.querySelector("title").textContent;
替换为:
/* 若为null或undefined,则赋值 */
documentMeta.title = documentMeta.title ?? document.querySelector("title").textContent;
/* 从文档中选择"title"元素的文本内容 */
全屏模式 退出全屏
这其实不是个元标签,但我认为这是一个有用的字段,所以我们会把它加入API中。 :)
我们的名为 getMetaTags
的函数应如下所示:
import { DOMParser, Element } from "jsr:@b-fuze/deno-dom";
// 获取元标签的异步函数
const getMetaTags = async (url: string) => {
// 设置请求头
const headers = new Headers();
headers.set("accept", "text/html,application/xhtml+xml,application/xml");
// 发送请求并获取响应
const res = await fetch(url, { headers });
// 获取响应的文本内容
const html = await res.text();
// 解析HTML
const document = new DOMParser().parseFromString(html, "text/html");
// 获取所有的meta标签
const metaTags = document.querySelectorAll("meta");
// 使用reduce方法处理每个meta标签
const documentMeta = (Array.from(metaTags) as Element[])
.reduce((acc, meta) => {
// 获取meta标签中的property属性
const property = meta.getAttribute("property");
// 获取meta标签中的name属性
const name = meta.getAttribute("name");
// 获取meta标签中的content属性
const content = meta.getAttribute("content");
// 如果没有content属性,则直接返回acc
if (!content) return acc;
// 如果有property属性,则将其添加到acc中
if (property) acc[property] = content;
// 如果有name属性,则将其添加到acc中
if (name) acc[name] = content;
return acc;
}, {} as Record<string, string>);
// 如果没有title,则从文档中获取title标签的内容
documentMeta.title ??= document.querySelector("title").textContent;
// 返回处理后的meta标签数据
return documentMeta;
};
进入全屏 退出全屏
这台服务器为了简单起见,我决定使用 Deno 内置的 http 服务器,这只是一个简单的 Deno.serve()
调用。
由于 Deno 是基于 web 标准构建的,我们可以使用 Fetch API 中内置的 Response 对象,来回应请求。
Deno.serve({ port: 8000 }, async (request: Request): Promise<Response> => {
const url = new URL(request.url);
// 如果请求方法是GET并且路径是/api/meta
if (request.method === "GET" && url.pathname === "/api/meta") {
// 获取URL参数中的url值并获取元标签
const metaTags = await getMetaTags(url.searchParams.get("url"));
// 设置响应头
const headers = new Headers();
headers.set("Content-Type", "application/json");
headers.set("Access-Control-Allow-Origin", "*");
// 返回JSON格式的元标签
return new Response(JSON.stringify(metaTags), { status: 200, headers });
}
// 如果没有找到资源,返回404错误
return new Response("not found", { status: 404 });
});
切换到全屏模式,退出全屏
我们的服务器解析请求URL,检查是否收到了针对/api/meta
路径的GET
请求,然后调用我们创建的getMetaTags
函数,并将元标签作为响应体返回。
我们还添加了两个头部,第一个是 Content-Type
,这样客户端就能知道响应中数据的类型,在我们这里,响应是 JSON 格式。
第二个头部是 Access-Control-Allow-Origin
,它允许特定来源的请求,在我们的情况下,我选择了 "*"
来允许任何来源,但你可能希望改为仅允许来自前端的请求。
注意,CORS 头部只会对浏览器发出的请求产生影响,这意味着浏览器会根据头部中指定的来源阻止该请求,但直接从服务器调用 API 仍然不受影响。更多关于 CORS 的信息可以在这里查看:这里。
现在你可以点击"保存并部署"
然后等待Deno完成部署你的代码到代码沙盒:
右上角的URL就是你的代码沙盒网址,复制它并添加/api/meta?url=https://dev.to
来查看它是否正常运作,URL应该看起来像https://metatags.deno.dev/api/meta?url=https://dev.to
你现在应该能看到API返回了dev.to的元标签!
使用 Deno 部署的沙盒意味着你的代码已经部署了,它是公开的,任何人都可以访问。
对于一个简单的像我们正在构建的这样的 API,单个文件的沙盒就足够了,但很多情况下我们希望扩展项目。
为此,你可以使用 Deno 部署的 Github 导出为你的 API 创建一个正式的代码仓库,并支持在代码推送后自动构建:
或者通过沙盒的设置:
本文中介绍的抓取技术仅适用于从服务器返回的 HTML 文件中包含元标签的网站,这样的网站更可能返回正确的结果。单页应用也可以正常工作,前提是它们在构建时而非运行时设置了元标签。
最后我们展示了如何快速简单地使用Deno构建和部署一个API,介绍了Meta标签(元标签),并展示了如何使用Fetch API、DOM解析器以及Deno自带的服务器仅用不到40行代码构建一个抓取Meta标签的API。
要查看本文中提到的项目,您可以查看 Deno 部署演示平台(您需要在右侧的 URL 栏里加上 /api/meta?url=https://dev.to
才能查看示例响应)或这个 GitHub 仓库。
zh: ……
你接下来要建什么呢希望这篇文章能激励你探索 meta 标签和 Deno 的强大功能!试着构建你自己的 API,或者将其集成到类似的项目中,比如书签管理器。
遇到问题或卡住了,或者想展示你建造的东西?下面留言,或者在Twitter/X上联系我——我很想听听你的分享!
看看我之前写的关于在不到40行代码的情况下构建React状态管理库插件的帖子这里。
共同学习,写下你的评论
评论加载中...
作者其他优质文章