为了账号安全,请及时绑定邮箱和手机立即绑定

仅用不到40行代码构建一个元数据标签抓取API

标签:
Python 爬虫 API

你有没有注意到,比如 Whatsapp 或 Telegram 这样的消息应用,是如何让你预览你发送的链接的?

WhatsApp 链接预览


这是一张图片的链接。

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中的&lt;meta&gt;元素表示无法通过其他HTML元数据相关元素(如&lt;base&gt;、&lt;link&gt;、&lt;script&gt;、&lt;style&gt;或&lt;title&gt;)表示的元数据。">
    <meta property="og:image" content="https://developer.mozilla.org/mdn-social-share.cd6c4a5a.png">

进入全屏模式,退出全屏模式

第一个标签是提供了页面的描述,而第二个标签是Open Graph标签(OG标签),定义了在社交媒体上分享时显示的图片。

一个实用的例子是用元标签来建立书签管理系统,这样就不用手动输入每个书签的标题、描述和图片,可以自动从书签网址抓取这些元信息。

开放图协议

开放图谱是一个互联网协议,最初由 Facebook 创建,旨在标准化网页元数据的使用,以表示页面内容,它有助于社交网络生成丰富的链接预览。
更多详情请参阅 这里

为什么是 Deno?
  1. Deno 采用安全的默认设置,这意味着它需要显式的权限来访问文件、网络和环境,减少了安全漏洞的风险。
  2. Deno 基于 web 标准构建,使用 ES 模块,并且旨在使用 Web 平台 API(如 fetch)而不是特定的 API,使得 Deno 代码看起来很像你在浏览器中编写的代码——但仍然有一些规范差异与浏览器。
  3. Deno 内置了对 TypeScript 的支持,允许你编写 TypeScript 代码而无需构建步骤。
  4. Deno 配备了一个标准库,包含了用于常见任务(如 HTTP 服务器、文件系统操作等)的模块。
  5. Deno 提供了一个代码检查器、格式化工具和测试运行器,让你可以使用该平台,而无需依赖第三方包或工具,使其成为全能的工具。
  6. 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
右上角的URL就是你的代码沙盒网址,复制它并添加/api/meta?url=https://dev.to来查看它是否正常运作,URL应该看起来像https://metatags.deno.dev/api/meta?url=https://dev.to
你现在应该能看到API返回了dev.to的元标签!
API响应

部署阶段

使用 Deno 部署的沙盒意味着你的代码已经部署了,它是公开的,任何人都可以访问。
对于一个简单的像我们正在构建的这样的 API,单个文件的沙盒就足够了,但很多情况下我们希望扩展项目。
为此,你可以使用 Deno 部署的 Github 导出为你的 API 创建一个正式的代码仓库,并支持在代码推送后自动构建:
Github 按钮
或者通过沙盒的设置:
来自设置的 GitHub 按钮

需注意的事项如下

本文中介绍的抓取技术仅适用于从服务器返回的 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状态管理库插件的帖子这里

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消