複数RSS取得して表示するPHP+JS ※Atom RSS1.0 RSS2.0対応【Google Feed API代替】

  • このエントリーをはてなブックマークに追加
  • Pocket
  • LINEで送る
RSS_PHP_JS
 Google Feed APIがダウンするというちょっとした騒ぎがありました。無料ですし便利なので今まで何かと使ってきましたが、突然ダウンされるもの困りますので、Google Feed APIの代替になるPHPを用意しました。複数のRSS読み込んでPHP+JavaScriptでwebサイトに表示します。



【更新履歴】
2019/02/20 RSS取得サイトのURL修正


 こんにちは!gaito777です。

 Google Feed APIがダウンすると地味に痛いです。便利ですし思ったより使っている人多かったようで、ダウン直後ネット上では阿鼻叫喚でした(笑)。ついにサービスが終了したのではないかと憶測を呼びましたが、結局いつのまにかしれっと復活してて逆に驚きました。

 公式サイトによると実は2015年4月20日までサポート終了だったようですね。まったく知りませんでした。

Google Feed API Terms of Service  |  Google Feed API  |  Google Developers

 そういう事情でGoogle Feed APIはもう当てにできなくなったので、代わりに用意したのが今回のPHPです。



概要

複数RSSを取得して表示するPHP − 概要

特徴


Atom RSS1.0 RSS2.0に対応。
複数のRSSを読み込んで記事を日時の順にソートし記事数を指定して表示できる。
各RSSの読み込む記事数を個別に設定できる。
RSSデータはテキストファイル(json形式)に保存するので頻繁にRSS配信元にアクセスしない。


 複数のRSSを読み込めますので、いろんな用途で使えると思います。


内容


サンプルページ


RSS sample page

RSSサンプルページ – ウェブソク


機能は大きく分けて2つ


RSSファイルリスト

 サンプルのファイルリストは上のようになるのですが、重要な機能は大きく分けて2つです。一つはget_rss.phpでRSSのデータファイルdata_rss.jsonを保存する仕組みです。もう一つはそれをdisp_rss.jsを使ってhtmlファイルindex.htmlに表示する仕組みです。

複数のRSSを読み込んでjsonに変換して保存(get_rss.php)
jsonファイルを読み込んでhtmlに追加(disp_rss.js)


 登録したRSSを読み込んでデータファイル(json形式テキストファイル)に保存しますので、これがキャッシュの代わりになって、大量のリクエストあった場合でもRSS配信元に一々アクセスすることなく処理することができます。RSS配信元の負担を減らすことと、RSSの表示スピードを速くすることができます。


RSSの取得にはcronを使おう


 そしてここで注意ですが、このままではget_rss.phpは自動で動きません。get_rss.phpを何らかの手段で定期的に動かして、RSSのデータファイルdata_rss.jsonを更新する必要があります。

 1番簡単なのはレンタルサーバーの機能でよくあるcronを使うのが良いと思います。

cronとは?

指定時間に指定コマンド(プログラム)を自動的に実行させるサーバーの機能です。
定期的なメールの送信や、ブログのRSS取得などに使用されます。


 大抵のどこのレンタルサーバーにもある機能なので、特に問題はなく使えると思います。使い方もそれぞれヘルプがありますので、参考にして設定してみて下さい。

 1時間毎や30分毎に動作させるなど、動かすタイミングを自由に設定できますので、RSSの更新頻度などに合わせて設定して下さい。


 cronの設定方法は、例えばXSERVERの場合だと、こんな感じになります。

Cron設定 – マニュアル|エックスサーバー



ソース

RSS PHP source

PHP


 RSSのURLの他、$entry_cntを設定することにより、各RSSから取得する記事数を個別に設定できます。最後に取得した全記事を日時で並べ替え出力します。

 $json_fileは、取得したRSSを保存するデータファイルです。PHPから利用しますので絶対パスで設定して下さい。このファイルが存在しない場合は自動的に作られます。

 $no_imageは、RSSから画像を取得できなかった場合のダミー画像です。ブラウザから利用しますのでURLで設定して下さい。

 先頭の#!/usr/local/bin/phpは、この記述がないとcronが動かない場合があるそうなので、念のため追加しています。レンタルサーバーによって違う場合があるかもしれませんので、動かない時は確認をお願いします。

get_rss.php

#!/usr/local/bin/php
<?php
//フィード($entry_cntは取得する記事数)
$feed&#91;&#93; = 'https://gigazine.net/news/rss_2.0/'; $entry_cnt&#91;&#93; = 5; //Gigazine
$feed&#91;&#93; = 'https://www.gizmodo.jp/index.xml'; $entry_cnt&#91;&#93; = 5; //GIZUMODO
//rss(json)保存ファイル(絶対パス)
$json_file = "/home/◯◯◯/◯◯◯/data_rss.json";

//画像がない場合表示(url)
$no_image = "http://◯◯◯.jp/◯◯◯/no_available_image.jpg";

$add_time = 1;

$outdata = Array();
$response = multiRequest($feed);

for ($i = 0; $i < count($response); $i++) {

    //パースエラー対策
    $response&#91;$i&#93; = mb_convert_encoding($response&#91;$i&#93;, "utf8", "auto");
    $search = array("\0", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x0b", "\x0c", "\x0e", "\x0f");
    $response&#91;$i&#93; = str_replace($search, '', $response&#91;$i&#93;);
    $rssdata = simplexml_load_string($response&#91;$i&#93;, 'SimpleXMLElement', LIBXML_NOCDATA);
    $count = 1;

    if ($rssdata != false) {

        // Atom
        if ($rssdata->entry) {
            $site = $rssdata->title;
            foreach ($rssdata->entry as $entry) {
                $title = $entry->title;

                if (isset($entry->issued)) {
                    $date_raw = $entry->issued;
                } else if (isset($entry->published)) {
                    $date_raw = $entry->published;
                } else if (isset($entry->updated)) {
                    $date_raw = $entry->updated;
                }
                date_default_timezone_set('Asia/Tokyo');
                $date_gnu = strtotime($date_raw);
                $date = date('Y/m/d - G:i', $date_gnu);

                $link = $entry->link['href'];

                $content = $entry->content;
                $img_url = "";
                $pattern = '/(ttps?)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)\.(jpg|png)/';
                if (preg_match_all($pattern, $content, $matches)) {
                    foreach ($matches[0] as $key => $value) {
                        $img_url = "h{$value}";
                    }
                } else {
                    $img_url = $no_image;
                }

                $array = array(
                    "site" => $site,
                    "title" => $title,
                    "url" => $link,
                    "date" => $date,
                    "image" => $img_url
                );

                if (array_key_exists($date_gnu, $outdata)) {
                    $date_gnu = $date_gnu + $add_time;
                    $add_time++;
                }
                $outdata[$date_gnu] = $array;

                if ($entry_cnt[$i] == $count) {
                    break;
                }
                $count++;
            }
        }


        // rss1.0
        elseif ($rssdata->item) {
            $site = $rssdata->channel->title;
            foreach ($rssdata->item as $item) {
                $title = $item->title;

                $date_raw = $item->pubDate;
                if (!$date_raw)
                    $date_raw = $item->children("http://purl.org/dc/elements/1.1/")->date;
                date_default_timezone_set('Asia/Tokyo');
                $date_gnu = strtotime($date_raw);
                $date = date('Y/m/d - G:i', $date_gnu);

                $link = $item->link;

                $content = $item->children("http://purl.org/rss/1.0/modules/content/")->encoded;
                $img_url = "";
                $pattern = '/(ttps?)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)\.(jpg|png)/';
                if (preg_match_all($pattern, $content, $matches)) {
                    foreach ($matches[0] as $key => $value) {
                        $img_url = "h{$value}";
                    }
                } else {
                    $img_url = $no_image;
                }

                $array = array(
                    "site" => $site,
                    "title" => $title,
                    "url" => $link,
                    "date" => $date,
                    "image" => $img_url
                );
                if (array_key_exists($date_gnu, $outdata)) {
                    $date_gnu = $date_gnu + $add_time;
                    $add_time++;
                }
                $outdata[$date_gnu] = $array;

                if ($entry_cnt[$i] == $count) {
                    break;
                }
                $count++;
            }
        }


        // RSS2.0
        elseif ($rssdata->channel->item) {
            $site = $rssdata->channel->title;
            $rssdata = $rssdata->channel;
            foreach ($rssdata->item as $item) {
                $title = $item->title;

                $date_raw = $item->pubDate;
                if (!$date_raw)
                    $date_raw = $item->children("http://purl.org/dc/elements/1.1/")->date;
                date_default_timezone_set('Asia/Tokyo');
                $date_gnu = strtotime($date_raw);
                $date = date('Y/m/d - G:i', $date_gnu);

                $link = $item->link;

                $content = $item->description;
                $content02 = $item->children('content', true)->encoded;
                $img_url = "";
                $pattern = '/(ttps?)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)\.(jpg|png)/';
                if (preg_match_all($pattern, $content, $matches)) {
                    foreach ($matches[0] as $key => $value) {
                        $img_url = "h{$value}";
                    }
                } else if (preg_match_all($pattern, $content02, $matches)) {
                    foreach ($matches[0] as $key => $value) {
                        $img_url = "h{$value}";
                    }
                } else {
                    $img_url = $no_image;
                }

                $array = array(
                    "site" => $site,
                    "title" => $title,
                    "url" => $link,
                    "date" => $date,
                    "image" => $img_url
                );
                if (array_key_exists($date_gnu, $outdata)) {
                    $date_gnu = $date_gnu + $add_time;
                    $add_time++;
                }
                $outdata[$date_gnu] = $array;

                if ($entry_cnt[$i] == $count) {
                    break;
                }
                $count++;
            }
        }
    }
}

krsort($outdata);

//json形式に変換
$n = 0;
$output = '';

$length = count($outdata);

foreach ($outdata as $outdata) {
    $n++;
    $output.= json_encode($outdata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

    if ($n !== $length) {
        $output.= ',';
    } else {
        break;
    }
}

//jsonファイル生成
$html = '[' . $output . ']';
$json = fopen($json_file, 'w+b');
flock($json, LOCK_SH);
fwrite($json, $html);
fclose($json);

function multiRequest($data, $options = array()) {

    $curly = array();
    $result = array();
    $mh = curl_multi_init();

    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);

        if (is_array($d)) {
            if (!empty($d['post'])) {
                curl_setopt($curly[$id], CURLOPT_POST, 1);
                curl_setopt($curly[$id], CURLOPT_POSTFIELDS, $d['post']);
            }
        }

        if (!empty($options)) {
            curl_setopt_array($curly[$id], $options);
        }
        curl_multi_add_handle($mh, $curly[$id]);
    }

    $running = null;
    do {
        curl_multi_exec($mh, $running);
    } while ($running > 0);
    foreach ($curly as $id => $c) {
        $result[$id] = curl_multi_getcontent($c);
        curl_multi_remove_handle($mh, $c);
    }
    curl_multi_close($mh);
    return $result;
}
?>



Javascript


 こちらでjsonファイルを読み込んでタグに整形します。最終的な表示件数はこちらで設定します。

 データファイルはブラウザから利用しますのでURLで設定します。

disp.rss.js

$(function () {

    $.ajax({
        type: "GET",
        url: "http://◯◯◯.jp/◯◯◯/data_rss.json", //データファイル
        dataType: "json",
        success: function (json) {

            var num = 12;	//表示件数
            var HTML = "<ul class=\"rss\">\n";
            for (var i = 0; i < num; i++) {
                if (!json&#91;i&#93;) {
                    continue;
                }	//記事がない場合は次へ
                var title = textSlice(json&#91;i&#93;.title&#91;0&#93;);
                var url = json&#91;i&#93;.url&#91;0&#93;;
                var date = json&#91;i&#93;.date;
                var image = json&#91;i&#93;.image;

                HTML += "<li><a class=\"image\" href=\"" + url + "\" target=\"_blank\"><img src=\"" + image + "\" alt=\"" + title + "\"/></a>";
                HTML += "<p>" + date + " <a href=\"" + url + "\" target=\"_blank\">" + title + "</a></p><div class=\"clear\"></div></li>\n";
            }
            HTML += "</ul>\n";
            $("#rss-area").append(HTML);
        }
    });

});

function textSlice(text) {	//文字数制限
    var cnt = 30;
    var mark = '…';
    if (text.length > cnt) {
        text = text.slice(0, cnt) + mark;
    }
    return text;
}



HTML


 <div id="rss-area"></div>の中にRSSが追加されます。jQueryを使って追加していますので、jQuery本体は読み込んで下さい。あとはデザインに合わせてお好きにどうぞ。

index.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>RSS</title>
<meta name="keywords" content="RSS" />
<meta name="description" content="RSS" />
<link href="common/css/reset.css" rel="stylesheet" type="text/css" />
<link href="common/css/base.css" rel="stylesheet" type="text/css" />
<link href="style.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="common/js/jquery.min.js"></script>
<script type="text/javascript" src="disp_rss.js"></script>
</head>
<body>

<div id="container">
<div id="header">
<h1>RSS</h1>
<p class="subtitle">This page tests read RSS.</p>
</div>
<div id="main">

<div id="rss-area"></div>

</div>
</div><!-- #container -->
</body>
</html>



CSS


 CSSはRSSのタグに合わせて設定して下さい。下記はサンプルページのRSS部分の設定です。

style.css

@charset "UTF-8";

ul {
	margin-top: 40px;
	border-top: 1px #cccccc dotted;
}

li {
	clear: both;

	padding: 10px 0;
	border-bottom: 1px #cccccc dotted;
}

li img {
	float: left;
	width: 120px;
	height: 90px;
	margin: 0 10px 0 0;
}

li p {
	float: left;
}



まとめ


 もしcronが使えなかった場合、例えばindex.htmlindex.phpにして、別途タイムスタンプ記録用のファイルを1つ用意。これと現在時刻を比較して、設定時間以上ならget_rss.phpを読み込む、とかにすればいいですね。

 いろいろと応用が効いて便利だと思いますので、ぜひ試してみて下さい。

  • このエントリーをはてなブックマークに追加
  • Pocket
  • LINEで送る

SNSでもご購読できます。

コメントを残す

*

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください