⛈️

Cloudflare WorkersのHTMLRewriter を使って超簡単に他サイトのOGPを取得し自ブログに表示してみた!

2023/06/04に公開

初めに

Zennで記事を書いていると、外サイトのurlを入力するといい感じにOGPが表示されます。

https://u-yas.dev

↑こんな感じです。ちなみにこれは僕の個人ブログです。よかったら来てください!

そういえば、自分のサイトにOGPを設定する記事とかはよくあるけど他サイトのOGPを表示する方法とかあんまり見ないな・・・と思うようになりました。

ていうか自分のブログだとできてないじゃん・・・
before_my_blog

よし!実装しよう!

超簡単だと思ったら・・・

(o'ω'o) < OGPデータを手に入れるにはとりあえずhrefに設定しているサイトのhtml取ってくる -> htmlのパースをする -> metaタグのogpに関する情報を取ってくればええってことやろ。それならapi作らなくてもクライアントからfetchでデータ取得してくればええやないか!簡単簡単♫

cors_arujan

(o'ω'o) < CORSのこと完全に忘れてた・・・そりゃそうだ・・・

(o'ω'o) < じゃあ、APIからfetch->htmlをパース->フロントエンドにogpデータを返さないと・・・

(o'ω'o) < API作らんと・・・

(o'ω'o) < 自分のブログはCloudflare PagesでできてるからCloudflare Workersでささっと実装したいな・・・

(o'ω'o) < でもfreeプランのCloudflare Workersってcpu runtime limitsが10msしかないからな・・・

(o'ω'o) < htmlのパースとか実行したら10msじゃ絶対できないだろ・・・

(o'ω'o) < はぁ・・・

と半ばあきらめかけていました・・・このドキュメントを見るまでは・・・

https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/

https://blog.cloudflare.com/introducing-htmlrewriter/

(o'ω'o) !!!!

(o'ω'o) < なんか・・・

(o'ω'o) < いけそう!!!!

HTMLRewriterとは?

The HTMLRewriter class allows developers to build comprehensive and expressive HTML parsers inside of a Cloudflare Workers application. It can be thought of as a jQuery-like experience directly inside of your Workers application. Leaning on a powerful JavaScript API to parse and transform HTML, HTMLRewriter allows developers to build deeply functional applications.

要はCloudflare Workers用に作られたHTMLパーサーらしいです!まさに僕がいま一番欲しかったものです!!!

超簡単に実装できた・・・!!!!!

Cloudflare Workersに以下のようなコードをちょびっと書くだけでOGPデータを取得し、フロントエンド側に返すことができました!(フレームワークにはHonoを使っています)


// ~~ 他の色々なコード
class OGPParser {
  ogpTitle: string;
  ogpDescription: string;
  ogpImageUrl: string;
  ogpSiteName: string;

  constructor() {
    this.ogpTitle = "";
    this.ogpDescription = "";
    this.ogpImageUrl = "";
    this.ogpSiteName = "";
  }
  element(element: Element) {
    switch (element.getAttribute("property")) {
      case "og:title":
        this.ogpTitle = element.getAttribute("content") ?? "";
        break;
      case "og:description":
        this.ogpDescription = element.getAttribute("content") ?? "";
        break;
      case "og:image":
        this.ogpImageUrl = element.getAttribute("content") ?? "";
        break;
      case "og:site_name":
        this.ogpSiteName = element.getAttribute("content") ?? "";
        break;
      default:
        break;
    }
  }
}

// 3秒で考えたpath
app.get("/another-site/ogp", async (c) => {
  // ステータスコードとかは適当です。仕事ではちゃんと考えています(涙)
  const href = c.req.query("href");
  if (href === undefined) {
    return c.body("Bad Request", 400);
  }
  const decodedHref = decodeURIComponent(href);
  const siteRes = await fetch(decodedHref);
  if (!siteRes.ok) {
    return c.body("Not Found", 404);
  }
  const ogp = new OGPParser();
  new HTMLRewriter().on("meta", ogp).transform(siteRes);

  // CloudflareのCDN Cacheに乗せればWorkersのリクエスト数を抑えることができるのでCacheを設定する
  c.header("Cache-Control", `public, s-maxage=${1 * days}`);
  return c.json(ogp);
});

ちゃんと動いています。まさかライブラリを使わずCloudflare WorkersのAPIだけでできるとは思っても見ませんでした・・・

そして、フロントエンド側でごにょごにょします
      (^⌒⌒^)
       | i i i i i|   JSON色付中
       | i i i i i|    
      (;`・ω・)っ-O・゚・⌒) 
      /  つ━ゝ,.__.,ノ))
           l从从从从l
     | ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|

結果がこちら
jojoen_ogp

まとめ

まさかここまで簡単に、Cloudflare Workersだけで他サイトをfetchしてHTMLをparseする処理が書けるとは思いも見ませんでした・・・
しかもこれ、10msのcpu limitsの制限時間以内に処理が終了しているんです。早すぎる!!!一生ついていきます!!

そんな他サイトのOGPを表示する機能が実装された僕のブログはこちらになります!

https://u-yas.dev

たまにネタ記事やZennに乗せるほどでもない雑メモなんかを置いたりしています。是非見に来てください!

Discussion