ついにURLのogp出力に対応した

2021年9月1日(水) 2時20分35秒 | 570 view |

URLを貼り付けたらOGP情報があればそれを引っ張ってきて貼り付けるような仕様に対応しました

成果物

microCMS
https://microcms.io/

Nuxt.js



Qiitaなどのimgixを使うような動的OGPも行ける(※一部不安定)


microCMSではこのように記述すればOK

実装

ビルド時にbody内を正規表現でURLスキーム操作→URLが発見されたらogp情報を取得しに行くAPIでogpを抽出→ogp情報を付与したリッチリンクにreplaceという手順で行った。

正規表現でURLスキーム操作

let urlreg = /https?:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+/g;
let urls = body.match(urlreg); //記事内のURLが格納される


ogpを取得しに行く

let ogpdata  = await $axios.$get("getogp?url=" + url,{ timeout : 1000000 }); //動的の場合も考慮してタイムアウトは長めに


自作のphp製OGP抽出APIを作成してそこにaxiosでリクエストを投げる仕様
こんなふうに返ってくる


phpの中身はこんな感じ

<?php
header('Access-Control-Allow-Origin: *');
header( "Content-Type: application/json; charset=utf-8" );
//OGPを取得したいURL
$url = $_GET['url'];
$headers = [
    'Content-Type: application/form-json; charset=utf-8'
];
$ch = curl_init($url);// urlは対象のページ
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);// exec時に出力させない
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); 
curl_setopt($ch, CURLOPT_TIMEOUT, 400); //timeout in seconds
curl_setopt($ch, CURLOPT_FRESH_CONNECT,true);//キャッシュを利用しない
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);// リダイレクト許可
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);// 最大リダイレクト数
$html = curl_exec($ch);
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
//DOMDocumentとDOMXpathの作成
$dom = new DOMDocument;
@$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$xpath = new DOMXPath($dom);
//XPathでmetaタグのog:titleおよびog:imageを取得
$node_title = $xpath->query('//meta[@property="og:title"]/@content');
$node_image = $xpath->query('//meta[@property="og:image"]/@content');
$node_description = $xpath->query('//meta[@property="og:description"]/@content');
$node_favicon = $xpath->query('//link[contains(@rel,"icon")]/@href');
if ($node_title->length > 0 && $node_image->length > 0) {
    //タグが存在すればサムネイルとタイトルを表示してリンクする
    $title = $node_title->item(0)->nodeValue;
    $image = $node_image->item(0)->nodeValue;
    $description = $node_description->item(0)->nodeValue;
    $favicon = $node_favicon->item(0)->nodeValue;
    $output = array('title'=>$title, 'image'=>$image, 'description'=>$description,'favicon'=>$favicon );


    echo json_encode($output, JSON_PRETTY_PRINT);
    http_response_code( 200 ) ;
}else{
    http_response_code( 400 ) ;
}
?>

DOMDocumentとDOMXpathがとても便利だった
@$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));をつけないと文字化けするので注意

ogp:imageは絶対パス固定だが、faviconは絶対パス相対パスどちらかなので、そのバリデーションはフロントエンド側で対応

if(metadata.favicon){
  if(!metadata.favicon.match(/http/)){
    let faviurl = new URL(metadata.favicon,url);
    favicon = faviurl.href;
  }
}


最後にこのAPIで得られた情報をHTMLに変換して元あったURLにreplaceしてあげることで実現。

今後

なぜかaタグでくくるとすべてのDOMに適用されるバグが起きたのでonclickでの遷移にした。なんとかしたい。
自分のWebの作り方の問題だと思うが、VOXELCANVASではWebアプリ側の処理を待たずにOGPを返却している。なんだろうこれ。









あとやってから思ったが、これはCMS側で対応してくれてもいいなあと思った。
URLを貼り付けたらその時点でmicroCMS側でOGPを取得して、microCMSのAPIで返却されるときはOGP情報ごとjsonで返却してくれるみたいな。
今後のアップデートに期待