ジーズのPHPの課題制作において、自分が契約しているサービスの一覧をビジュアルで表示するという契約管理サイトを作成しました。
その中で、「リンク先のOGP画像を取得してサイト上に表示させる」というのをやりたかったのですが、調べた際に「自分のサイトにOGP画像を設定する」という記事は沢山あったのですが、OGP画像を取得するという記事があまりなく、苦労したのでまとめたいと思います。
OGP画像の取得方法
OGP画像取得関数の全体像
PHPファイルに以下の関数を記述して、OGPを取得したいURLを引数に呼び出す
function getOgpImg($url) {
// Step1: URLからHTMLを取得
$html = file_get_contents($url)
if ($html === false) {
throw new Exception('Error fetching the URL');
}
// Step2: DOMDocumentを使用してHTMLを解析して画像情報を取得
$doc = new DOMDocument();
@$doc->loadHTML($html);
$metaTags = $doc->getElementsByTagName('meta');
foreach ($metaTags as $meta) {
if ($meta->getAttribute('property') == 'og:image') {
$ogpImg = $meta->getAttribute('content');
}
}
// Step3: 取得した結果を返す
return $ogpImg;
}
コード解説
Step1: URLからHTMLを取得
$html = file_get_contents($url)
if ($html === false) {
throw new Exception('Error fetching the URL');
}
まず「file_get_contents」関数で、対象のURLのHTMLコードを取得する
例1 とあるウェブサイトのホームページのソースの取得と出力
https://www.php.net/manual/ja/function.file-get-contents.php
<?php
$homepage = file_get_contents('http://www.example.com/');
echo $homepage;
?>
falseを返すことがある(上記の公式ドキュメント参照)ので、エラーの場合の記述を入れる
Step2: DOMDocumentを使用してHTMLを解析して画像情報を取得
$doc = new DOMDocument();
「DOMDocument」クラスを使ってHTMLを解析します。
「new DOMDocument」で DOMDocumentオブジェクトを生成します。
HTML ドキュメントあるいは XML ドキュメント全体を表し、 ドキュメントツリーのルートとなります。
https://www.php.net/manual/ja/class.domdocument.php
@$doc->loadHTML($html);
loadHTMLでHTMLを読み込みますが、@を付けることで、HTML解析中に発生する警告を抑制しています。
DOMDocument::loadHTML — 文字列から HTML を読み込む
https://www.php.net/manual/ja/domdocument.loadhtml.php
$metaTags = $doc->getElementsByTagName('meta');
HTMLの中から「meta」と付いているタグを探しに行き、$metaTagsに格納します。
getElementsByTagName は Document インターフェイスのメソッドで、指定されたタグ名を持つ要素の HTMLCollection を返します。
https://developer.mozilla.org/ja/docs/Web/API/Document/getElementsByTagName
ちなみにOGP関連のタグはHTMLに以下のように記載されています。
(ジーズアカデミーのWebから取得しました。Descriptionは長かったので削ってあります)
<meta name="format-detection" content="telephone=no">
<meta name="description" content="冒険を、すべての人へ。プログラミングでセカイを変える。">
<meta name="keywords" content="プログラミング,Web,CODE,エンジニア,スクール,学校,スマホ,アプリ,制作,転職,就職,HTML,PHP,Javascript,起業,CSS,Laravel,Git,Github,スタートアップ">
<meta property="og:title" content="ジーズアカデミー|セカイを変えるGEEKになろう">
<meta property="og:type" content="website">
<meta property="og:url" content="https://gsacademy.jp/">
<meta property="og:image" content="https://gsacademy.jp/uploads/og_gsacademy_top.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:site_name" content="ジーズアカデミー|エンジニアと起業の学校">
<meta property="og:description" content="冒険を、すべての人へ。プログラミングでセカイを変える。">
これの「property=”og:image”」にある「content=””」の中にあるファイル名を取得したいのです。
foreach ($metaTags as $meta) {
if ($meta->getAttribute('property') == 'og:image') {
$ogpImg = $meta->getAttribute('content');
}
}
foreachで先ほどの$metaTagsを1つずつ見に行き、「property」が「og:image」と一致するものがあれば、その「content」の値を$ogpImg変数に格納します。
Step3: 取得した結果を関数の外に返す
// 取得した結果を返す
return $ogpImg;
この返した結果は「https://gsacademy.jp/uploads/og_gsacademy_top.png」のようなURL形式の画像ファイルなので、それを表示したい箇所にimgタグで埋め込めばHTMLとして表示が出来るようになります。
OGP画像を取得・表示するとサイトが重たくなる問題
以上で出来た!となったのですが、今回複数のURLをサイトに表示させる仕様だったこともあり、画面描写にローカルだと5〜10秒、デプロイしたレンタルサーバ上だと20〜30秒かかってしまい、重くなることが課題でした。
おそらく、毎回HTMLのソースコードを取得してmetaタグを探して…という処理をやっていることが重たい原因だと想定されたため、色々試行錯誤の結果、OGP画像のURL情報をDBに格納して、画面描写の際にそのDBからOGP画像のURLを取得して描写という処理にすることで、ほぼ待ち時間なく描写されるようになりました。
そもそも契約管理サイトとして、契約しているサービスやプランなどをDBに登録するので、その一環で入れました。
ちなみに、画像が直リンクになってしまうのも気になっていて(インターネット創世記にHP作ってた人間としては絶対禁止という感覚があってですね)、当初重たいのは直リンクのせいもあるかなと思ったのですが、例えばnotionでURLを貼り付けるとブックマークが作れるのですが、その際もOGP画像が表示されていたりするわけで、そもそもOGP画像自体は直リンクで表示されるものではあると思うので、直リンク自体は残すことにしました。