🖼️

フロントエンドで OGP の情報を取得する

2024/03/10に公開

OGP

OGP (Open Graph protocol) とはページの情報を外部のサイトへ伝えるための仕組みです。
タイトルやサムネイルの情報を HTML 内の <meta> タグに記載します。

https://ogp.me/

CORS

外部サイトの OGP 情報を取得しようとしたときにぶつかるのが CORS エラーです。
CORS とはセキュリティの兼ね合いでブラウザーがオリジン(平たく言うとドメイン)をまたいだリクエストのレスポンスに、フロントエンドの JavaScript コードがアクセスすることをブロックする仕組みです。

サーバーは Access-Control-Allow-Origin: * ヘッダーを付けることでアクセスを許可することもできますが、すべてのサイトがこの設定をしているとは限りません。

詳細は MDN をご参照ください。

https://developer.mozilla.org/ja/docs/Glossary/CORS

CORS Proxy

CORS はブラウザーによる制限なので別のサーバーを経由してアクセスすることで回避することができます。

サーバーで HTML を取得し DOM をパースした結果を返す[1]のが一般的かと思いますが HTML をそのまま返してフロントエンドで DOM をパースすることもできます。

HTML をそのまま返す proxy サーバーを自分でホスト[2]しても良いのですが提供してくれるサービスがあるので今回はこちらを使いましょう。

https://corsproxy.io/

DOMParser

HTML の取得ができたら後は DOM をパースして OGP の情報を探すだけです。
Node.js だと jsdom を使うようですが Web API には DOMParser が用意されています。

https://developer.mozilla.org/ja/docs/Web/API/DOMParser

OGP 情報を取得するコード

以上を踏まえて実際に OGP を取得するコードです。

const url = 'https://zenn.dev/';
const proxyUrl = `https://corsproxy.io/?${encodeURIComponent(url)}`;
const response = await fetch(proxyUrl);
const html = await response.text();
const domParser = new DOMParser();
const dom = domParser.parseFromString(html, 'text/html');
const ogp = Object.fromEntries(
    [...dom.head.children]
        .filter(
            (element) =>
                element.tagName === 'META' &&
                element.getAttribute('property')?.startsWith('og:')
        )
        .map((element) => {
            return [
                element.getAttribute('property'),
                element.getAttribute('content')
            ];
        })
);
console.log(ogp);
例) Zenn
{
    "og:url": "https://zenn.dev",
    "og:title": "Zenn|エンジニアのための情報共有コミュニティ",
    "og:image": "https://static.zenn.studio/images/logo-only-dark.png",
    "og:description": "Zennはエンジニアが技術・開発についての知見をシェアする場所です。本の販売や、読者からのバッジの受付により対価を受け取ることができます。",
    "og:type": "article",
    "og:site_name": "Zenn"
}
おまけ: Twitter Card
const twitterCard = Object.fromEntries(
    [...dom.head.children]
        .filter(
            (element) =>
                element.tagName === 'META' &&
                element.getAttribute('name')?.startsWith('twitter:')
        )
        .map((element) => {
            return [
                element.getAttribute('name'),
                element.getAttribute('content')
            ];
        })
);
console.log(twitterCard);
脚注
  1. 参考: https://zenn.dev/littleforest/articles/scrape-og-tags, https://zenn.dev/uyas/articles/0b7dcbb46d8031 ↩︎

  2. 参考: https://zenn.dev/tm35/articles/27be33a239a687 ↩︎

Discussion