クロスドメイン下でphpを使わずにwpの記事一覧を取得する方法

できたったよ

前提:やりたいことと制限の話

makeshopでWEBサイトを作成することになり、サイト内コンテンツとして新着情報とかブログを付けたいという話になった。makeshopにはそのどちらも機能がない(新着情報はあるっちゃある)んで、wpで作ろうってことになった。makeshop内でwpは作れないけどそこに関してのクロスドメインは問題ない。

wpから記事一覧を取得する方法はいくつかあるけど、googleとかyahooとかが取得できるapiサービスを展開してたけど終了してしまった。クロスドメイン下でのRSS取得は、JSではできないっていう仕様がある。セキュリティの問題がどうのこうのって話で、詳しいことはめんどいので割愛。

phpであればRSSを拾ってこれるらしいけどmakeshopにphpは組み込めない。RSS配布側で一覧を作ってiframeで読み込めばなんとかなるんだけど、iframeはiphoneだと勝手が悪いんであんまり使いたくない。デザインがぶっ壊れるっていうのが何年も前から言われてるけど、未だにそのままっぽい。

jsだけが使える静的ページ(.html)でWPの記事一覧を取得・表示する方法を本ページでやっていく。

WPの記事一覧取得について

記事一覧の取得といえばfeedとかrssとかが挙がるけど、jsonでも取得できる。

一般的なやつ
https://ドメイン/wp-json/wp/v2/posts
サムネ画像とか込み
https://ドメイン/wp-json/wp/v2/posts?_embed

とりあえず、URLを置換してブラウザで開けば中身が覗けるのでどんな感じか分かる。

組み込みは多次元連想配列で格納されてるのを取得して必要なものを拾って表示させるって感じ。

結論の1つとしてここで言いたいのが、RSSはクロスドメインだと制限がかかるけどjsonだったら無条件で表示までもっていける。RSSとJSONで何が違うんだろうね。

JSON取得のURLについて

WPには初期状態で設置されてる「投稿」とプラグインなり何なりで用意する「カスタム投稿」がある。RSSよろしく、JSONでもそれぞれを分けて取得できる。

通常投稿
https://ドメイン/wp-json/wp/v2/posts
カスタム投稿「news」
https://ドメイン/wp-json/wp/v2/news

通常投稿・カスタム投稿問わず、取得内容に条件をつけられる。
詳しくはこちらの記事でいい感じにまとめられてる。

で、カスタム投稿なんだけど、「CPT UI」で作成したカスタム投稿は別段なにか設定をする必要もなく、そのままJSONを拾える。サービスが終わったり更新が途絶えたりしたらリスクだからプラグイン否定派の気持ちもわかるけど、やっぱ楽ですね。

導入例

こんな感じ。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script>
    $(function(){
        $.ajax({
            type: 'GET',
            url: 'https://ドメイン/wp-json/wp/v2/posts',
            dataType: 'json'
        }).done(function(json){
            var len = json.length;
            for(var i=0; i < len; i++){
                var title = json[i].title.rendered;
                var link = json[i].link;
                var excerpt = json[i].excerpt.rendered;
                var pubDD = new Date(json[i].date);
                yy = pubDD.getYear();if (yy < 2000) { yy += 1900; }
                mm = pubDD.getMonth() + 1;dd = pubDD.getDate();
                var pubDate = yy +'.'+ mm +'.'+ dd ;
                var html = '<li><span>' + pubDate + '</span><a href="' + link + '">'+ title +'</a>' + '</li>'+ excerpt ;
                $('.data').append(html);
            }
        }).fail(function(json){
            $('.data').append("読み込みませんでした。");
        });
    });
   </script>
</head>
<body>
<ul class="data"></ul>
</body>
</html>

jQuery1~3で動作確認済み。
ローカル上でも取得できるから動作確認が楽。

jsonの取得条件によって読み込める内容が変わるから注意しましょうねって感じ。
わからんかったら全取得状態にしておけばとりあえず大丈夫っていう。

「len」は表示数の上限指定。コードではJSONに存在する全部をカウントしてるんで注意。

アイキャッチ画像の取得方法

通常投稿からアイキャッチ取得は上記からちょっと手を加えることになる。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script>
    $(function(){
        $.ajax({
            type: 'GET',
            url: 'https://blog.kofu-jewelry.jp/hiroesano/wp-json/wp/v2/posts?_embed',  //「?_embed」を足す
            dataType: 'json'
        }).done(function(json){
            var len = json.length;
            for(var i=0; i < len; i++){
                var title = json[i].title.rendered;
                var link = json[i].link;
                var excerpt = json[i].excerpt.rendered;
                var img = json[i]._embedded['wp:featuredmedia'][0].source_url; //画像URLのところを掘り下げて指定する
                var pubDD = new Date(json[i].date);
                yy = pubDD.getYear();if (yy < 2000) { yy += 1900; }
                mm = pubDD.getMonth() + 1;dd = pubDD.getDate();
                var pubDate = yy +'.'+ mm +'.'+ dd ;
                var html = '<li><span>' + pubDate + '</span><a href="' + link + '">'+ title +'</a>' + '</li>'+ excerpt +'<img src="'+ img +'">'; //画像を表示
                $('.data').append(html);
            }
        }).fail(function(json){
            $('.data').append("読み込みませんでした。");
        });
    });
   </script>
</head>
<body>
<ul class="data"></ul>
</body>
</html>

前例に倣って連想配列のkeyで繋いでいくと「wp:featuredmedia」で躓くことになる。
そのままだと使えないんで、「[‘wp:featuredmedia’][0]」と書く。

これはアイキャッチ画像をそのまんま取得するように書いてるんで、the_post_thumbnail()で指定できるサイズも格納されてるんで、そっちを使いたかったらもっと改装を掘り下げる。

JSONの確認

タイトル・日付・URLがあればひとまず足りると思うけど、アイキャッチ画像よろしく他の要素も表示したいってなる場合もある。で、jsonを表示すると改行とかインデントの無い状態になってるんで、人の目にはすごく見づらい。手作業で改行作業をするのも面倒が勝つ。

WEB上にはコード整形を公開してくれてるところがいくつかあるのでそれを利用するのが手っ取り早い。整形ツール自体にコードが指定されてる場合もあるので気をつける。

JSONきれい ~JSON整形ツール~ - instant tools

コピペして変換を押せば出る。

違和感

RSSはクロスドメインに制限があるのに、なんでJSONはクロスドメインをクリアできたのか。WPの仕様なのか、テーマとかでたまたま環境が許可される状態になってるのか、そもそもそういうものなのか。

ぶっちゃけた話、理由がわからない。調べてみたら制限があるとかJSONPならいけるよとか色々書いてるけど、現状CORSで指定してない別サーバー・別ドメインに突っ込んでも動作してる。

どういうことなんだろうか。

カスタム投稿のJSON取得

JSONのURLは上記にあるとおりだけど、とりあえず、通常投稿とカスタム投稿はJSONの中身が違うことがあるということを念頭に置いておく。アイキャッチ画像を設置してないのにアイキャッチを呼び出そうとするとエラーが出る。抜粋文が無いのに呼び出そうとするとエラーが出る。単純に掲載してないからだったり、カスタム投稿に不要だから存在しない、とか理由は様々ある。だから呼び出す際の記述が異なる部分があるんで気をつける。

存在しないものを呼び出そうとするとエラーが出る。
存在しないものを呼び出そうとするとエラーが出る。
存在しないものを呼び出そうとするとエラーが出る。

まあでもほぼ一緒なんで、まんま使えると決めてかかったらちょっとだるいよって話。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script>
    $(function(){
        //通常投稿
        $.ajax({
            type: 'GET',
            url: 'https://ドメイン/wp-json/wp/v2/posts?_embed',
            dataType: 'json'
        }).done(function(json){
            var len = json.length;
            for(var i=0; i < len; i++){
                var title = json[i].title.rendered;
                var link = json[i].link;
                var excerpt = json[i].excerpt.rendered;
                var img = json[i]._embedded['wp:featuredmedia'][0].source_url;
                var pubDD = new Date(json[i].date);
                yy = pubDD.getYear();if (yy < 2000) { yy += 1900; }
                mm = pubDD.getMonth() + 1;dd = pubDD.getDate();
                var pubDate = yy +'.'+ mm +'.'+ dd ;
                var html = '<li><span>' + pubDate + '</span><a href="' + link + '">'+ title +'</a>' + '</li>'+ excerpt +'<img src="'+ img +'">';
                $('.data').append(html);
            }
        }).fail(function(json){
            $('.data').append("読み込みませんでした。");
        });
        //カスタム投稿(news)
        $.ajax({
            type: 'GET',
            url: 'https://ドメイン/wp-json/wp/v2/news',
            dataType: 'json'
        }).done(function(json_news){
            var len = json_news.length;
            for(var i=0; i < len; i++){
                var title = json_news[i].title.rendered;
                var link = json_news[i].link;
                var pubDD = new Date(json_news[i].date);
                yy = pubDD.getYear();if (yy < 2000) { yy += 1900; }
                mm = pubDD.getMonth() + 1;dd = pubDD.getDate();
                var pubDate = yy +'.'+ mm +'.'+ dd ;
                var html = '<li><span>' + pubDate + '</span><a href="' + link + '">'+ title +'</a>' + '</li>';
                $('.data_news').append(html);
            }
        }).fail(function(json_news){
            $('.data_news').append("読み込みませんでした。");
        });
    });
   </script>
</head>
<body>
    <ul class="data"></ul>
    <ul class="data_news"></ul>
</body>
</html>

JSONの記述有無チェック

JSONは多次元連想配列なわけだけど、keyに値がないって場合、空欄であったりkeyそのものが無かったりする。keyが存在しない場合、抽出するためのコードはエラーを吐いて処理が止まる。具体的には、アイキャッチが登録されてないのにアイキャッチを取得しようとしたらエラーになる。

WPのアイキャッチの処理は投稿内容にアイキャッチがなかった場合は表示しないor代替画像を表示するって感じで、投稿内容自体に対してのフォローは無くて、表示側のフォローで対応されてる。

URLとか投稿日時とかは記事とともに確実に存在するからいいんだけど、そういう存在があやふやなものを取得する場合には条件分岐を入れておく必要がある。

            for(var i=0; i < len; i++){
                var title = json[i].title.rendered;
                var link = json[i].link;
                if((['wp:featuredmedia'][0] in json[i]._embedded)== true){
                    var img = json[i]._embedded['wp:featuredmedia'][0].source_url;
                }else{
                    var img = '【代替画像】';
                }
                var pubDD = new Date(json[i].date);
                yy = pubDD.getYear();if (yy < 2000) { yy += 1900; }
                mm = pubDD.getMonth() + 1;dd = pubDD.getDate();
                var pubDate = yy +'/'+ mm +'/'+ dd ;
                var html = '<li><span>' + pubDate + '</span><a href="' + link + '">'+ title +'</a>' + '</li>'+ excerpt +'<img src="'+ img +'">';
                $('.data').append(html);
            }

これでいうと何かしら画像は確定で表示したいから、「img」に何を突っ込むかっていうのを条件分岐で作成。

条件分岐のifはkeyの有無で行う。アイキャッチは深い階層だから結構掘り下げることになったけど、titleとかだったら楽ですね。条件分岐参考記事はこちら。どのkeyが欲しいかによってifの内容が変わってくるんだけども、その有無によって極端に内容が変わる場合がある。表示する内容について色んなバリエーションで記事を書き出して、JSONがどう変わるのかを実際に確認したほうがいい。

20211130追記

JSONの知識がついた。

しかし、いきなり触ってjsで出力したのは今思えば中々すごいことだと思うよ。

20221215追記:サイズ指定とかサムネの画像を取得

上記のやつはフルサイズの画像を取得していた。でかいものが必要ない場合は重いだけ無駄なので、小さい画像を使いたい。その場合、こうなる。

//原寸サイズ
var img = json[i]._embedded['wp:featuredmedia'][0].source_url;

//medium
var img = json[i]._embedded['wp:featuredmedia'][0].media_details.sizes.medium.source_url;

「medium」のところをthumbnailにするとかでサイズを変えられる。当然だけど取得元が用意しているサイズに準拠してるので、こちらは拾いに行ってるだけなので、できるのは選ぶことだけ。

しばらくやっててわかったんだけど、何を言ってもバカでかい画像や不揃いをアップし続ける人がたくさんいるので面倒がすごい。だったらもう整形されたものを拾えばいいよねって。

コメント

タイトルとURLをコピーしました