Google Feed API の代替をJSとphpでなんとかする

fee2

様々なサイトで利用していたGoogle Feed APIが2017年1月に入って、ついに死んだ。
外部ブログ(例えばアメブロ)のRSSをGoogle Feed APIで取得し、
サイトに表示していた人は結構いるのではないだろうか。

諸事情により、下記の条件でRSSを取得して表示する必要が出てきたのでメモ。

実装要件

  • 複数のブログ(クロスドメイン)からRSSを取得
  • 各ブログからは最新記事1件を取得し投稿日順に並べる
  • 読み込み時の処理軽減必須
  • RSSからは記事タイトル サムネイル画像 投稿日 URL ブログタイトルの計5項目を取得

やったこと

結論からいうと、JS単品ではクロスドメイン(外部ブログ)の取得が困難だったので、
PHPにて対応を行った。
読み込み高速化のために、cURL multiを用いて並列読み込みをしつつ、
キャッシュ(Package Information:Cache_Lite)を利用する。
サーバーにインストールされていない場合は、上記URLからダウンロードし、
RSS取得phpと同階層に設置する。
また、キャッシュファイルを保存するディレクトリをrsscacheという名前で同階層に作成する。

ソースコード

rss.php

<?php
    //表示合計記事数
    $hyojiNum = 10;
    //フィード登録
    $data['feedurl'][] = 'https://feedURL1.com/rss.xml';
    $data['feedurl'][] = 'https://feedURL2.com/rss.xml';
    $data['feedurl'][] = 'https://feedURL3.com/rss.xml';
    $data['feedurl'][] = 'https://feedURL3.com/rss.xml';
    //  適宜追加

    $rssList = $data['feedurl'];

    //キャッシュ準備
    require_once('Cache/Lite.php');
    $cacheDir = 'rsscache/';
    $lifeTime = 60*60;
    $automaticCleaningFactor = 100;
    $options = array('cacheDir' => $cacheDir ,'caching' => true, 'lifeTime' => $lifeTime, 'automaticSerialization' => 'true','automaticCleaningFactor' => $automaticCleaningFactor);
    $cacheData = new Cache_Lite($options);
    $outdata =  $cacheData->get('rsscache');
    if(!$outdata) {

        //同時呼び出し
        $rssdataRaw = multiRequest($rssList);
        for($n=0;$n<count($rssdataRaw);$n++){
            $rssdata = simplexml_load_string($rssdataRaw[$n]);
            if($rssdata->channel->item) $rssdata = $rssdata->channel;
            if($rssdata->item){
                $site = $rssdata->title;
                $i = 0;
                $kiji = 1; //記事取得数
                foreach($rssdata->item as $myEntry){
                    if( $i >= $kiji){
                        break;
                    } else {
                    $rssDate = $myEntry->pubDate;
                    if(!$rssDate) $rssDate = $myEntry->children("http://purl.org/dc/elements/1.1/")->date;
                    date_default_timezone_set('Asia/Tokyo');
                    $myDateGNU = strtotime($rssDate);
                    $myDate = date('Y/m/d',$myDateGNU);
                    $coverImage = $myEntry->enclosure->attributes()->url;
                    $myBlogTitle = $site;
                    $myTitle = $myEntry->title; //タイトル取得
                    $myLink = $myEntry->link; //リンクURL取得
                    //出力内容(CSSOK)
                    if(preg_match('/PR:/',$myTitle)) continue;
                    $outdata[$myDateGNU] =  '<a href="' . $myLink . '" target="_blank">' . '<div class="feed_img" style="background-image: url('.$coverImage.');"></div>';
                    $outdata[$myDateGNU].=  '<div class="feed_text"><p><span>' . $myDate . '</span>';
                    $outdata[$myDateGNU].=  $myTitle . '</p><p class="feed_title">By ' . $myBlogTitle .'</p></div></a>';
                    $i++;
                    }
                }
            }
        }
        //ソート
        krsort($outdata);
        $cacheData->save($outdata,'rsscache');
    }
    $nn = 0;
    $html = '';
    foreach($outdata as $outdata) {
        $nn++;
        $html.= $outdata;
        if($nn == $hyojiNum) break;
    }
    $html = $html;
    echo $html;


    //同時呼び出し関数
    function multiRequest($data, $options = array()) {
        // array of curl handles
        $curly = array();
        // data to be returned
        $result = array();
        // multi handle
        $mh = curl_multi_init();
        // loop through $data and create curl handles
        // then add them to the multi-handle
        foreach ($data as $id => $d) {
        $curly[$id] = curl_init();
        $url = (is_array($d) && !empty($d['url'])) ? $d['url'] : $d;
        curl_setopt($curly[$id], CURLOPT_URL,            $url);
        curl_setopt($curly[$id], CURLOPT_HEADER,         0);
        curl_setopt($curly[$id], CURLOPT_RETURNTRANSFER, 1);
        // post?
        if (is_array($d)) {
        if (!empty($d['post'])) {
        curl_setopt($curly[$id], CURLOPT_POST,       1);
        curl_setopt($curly[$id], CURLOPT_POSTFIELDS, $d['post']);
        }
        }
        // extra options?
        if (!empty($options)) {
        curl_setopt_array($curly[$id], $options);
        }
        curl_multi_add_handle($mh, $curly[$id]);
        }
        // execute the handles
        $running = null;
        do {
        curl_multi_exec($mh, $running);
        } while($running > 0);
        // get content and remove handles
        foreach($curly as $id => $c) {
        $result[$id] = curl_multi_getcontent($c);
        curl_multi_remove_handle($mh, $c);
        }
        // all done
        curl_multi_close($mh);
        return $result;
    }
?>

htmlにRSSを表示したい場合

phpではなくhtmlに埋め込みたい場合は、
先のRSS取得phpに加えて以下のような感じで対応する。
※Jquery使います。

rss.html

<div id="feed"></div>
<script src="jquery.js"></script> 
<script src="rss.js"></script>

rss.js

$(document).ready(function(){
  // vars
  var rssURL = 'rss.php'; //rss.phpのディレクトリを指定
  var setDOM = $('#feed');  //表示させる箇所のDOM要素を指定

  // RSS取得開始
  $.ajax({
    url: rssURL,
    type:'GET',
    timeout: 5000,
    dataType: 'html',
    success: function(html){
      setDOM.append(html).hide().fadeIn(1000);
    }
  });
});

おわりに

本当であれば、php側でjsonに変換してしまい、js側で取得し出力するほうが個人的にやりやすいんだけど、今回は色々なサイト様に頼って上記のカタチに落ち着きました。

参考サイト