我厌倦了重复构建链接预览功能,于是把它做成了 API

发布: (2026年3月16日 GMT+8 02:01)
6 分钟阅读
原文: Dev.to

Source: Dev.to

Cover image for I got tired of building the same link preview function, so I made it an API

在过去的几年里,我构建的每个应用都需要同样的功能:粘贴一个 URL,显示预览卡片。Slack 这样做,Discord 也这样做,所有的 CMS 也是如此。每次我都要重新编写相同的 Cheerio 抓取代码,处理相同的 Open Graph 标签边缘情况,并且调试同样的问题——Twitter Cards 使用 name 而不是 property,而且网络上一半的人都写错了。

不久前,我终于把所有这些功能提取成一个独立的 API,并将其放在 RapidAPI 上。想想其他人也在写同样的代码。

实际返回内容

您传入一个 URL。您将获得跨六个层级的结构化元数据:

  • Open Graph – 标题、描述、带尺寸的图片、文章元数据
  • Twitter Cards – 卡片类型、站点、创建者;仅包含实际存在的标签,不进行回退猜测
  • HTML meta – title 标签、meta description、canonical、主题颜色
  • Icons – 自动选择质量最高的 favicon,使用优先级链
  • Feeds – 发现 RSS、Atom 和 JSON Feed 链接
  • JSON‑LD – 解析所有 script 块,优先使用 Article/Product 而非 BreadcrumbList

响应同时提供合并后的顶层视图(标题来源于有该信息的第一个源;先是 OG,然后是 Twitter,最后是 title 标签)以及原始解析层,以便您自行处理逻辑。

{
  "title": "GitHub · Build and ship software on a single, collaborative platform",
  "description": "Join the world's most widely adopted...",
  "image": {
    "url": "https://github.githubassets.com/images/modules/site/social-cards/campaign-social.png",
    "width": 1200,
    "height": 630
  },
  "favicon": "https://github.githubassets.com/favicons/favicon.svg",
  "siteName": "GitHub",
  "type": "website",
  "themeColor": "#1e2327",
  "openGraph": { ... },
  "twitter": { ... },
  "feeds": [],
  "jsonLd": { "@type": "WebSite", ... },
  "responseTime": 234
}

正确处理起来令人烦恼的部分

  • OG 标签使用 property,Twitter 使用 name
    Open Graph 规范写的是 ;Twitter Cards 写的是。很多站点会把它们互换使用,所以解析器会同时检查这两个属性的两种前缀。

  • 多个 og:image 标签是合法的。
    OG 规范通过重复标签来支持数组。像 og:image:width 这样的结构化属性会应用到最近声明的 og:image。大多数抓取器只会获取第一个并忽略其余的。

  • JSON‑LD 块非常混乱。
    一个典型的新闻文章页面可能包含多个 JSON‑LD 块(例如 BreadcrumbListOrganization 和实际的 Article)。需要解析所有块并挑选出正确的那个。

  • Favicons 有优先级顺序。
    Apple touch 图标(180×180)通常质量最高,其次是 32×32 的标准图标,最后是通用的 /favicon.ico 备选。大多数实现只会抓取它们找到的第一个 “。

  • 到处都是相对 URL。
    OG 图片和 feed 链接常常是相对路径。需要使用实际的 URL(包括重定向后的地址)作为基准来正确解析它们。

The technical approach

该服务运行在托管于 VPS 的 Fastify 服务器上,使用 Cheerio 进行 HTML 解析。没有使用无头浏览器或 Puppeteer——仅仅抓取 HTML 并解析,确保缓存未命中时响应时间保持在 500 ms 以下,缓存命中时低于 5 ms。

SSRF 防护是耗时最长的部分。由于 API 接受任意 URL,它首先解析主机名,将得到的 IP 与私有地址段的阻止列表进行比对,然后直接连接解析后的 IP,以防止 DNS 重绑定攻击。

我还在同一套基础设施上构建了一个 Text Analytics API:传入文本即可返回可读性评分(Flesch‑Kincaid、Coleman‑Liau、SMOG 等)、关键词密度、二元组、三元组以及估计阅读时间。全部是对字符串的纯数学运算,响应时间低于 10 ms——对内容优化工具和写作助手非常有用。

试用它们

两个 API 都在 RapidAPI 上,提供免费套餐(每月 500 次请求):

免费套餐足以进行测试和原型开发。如果你遇到解析器处理不佳的边缘情况,我真的很想了解——解析互联网上的混乱 HTML 是一个永无止境的项目。

0 浏览
Back to Blog

相关文章

阅读更多 »