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

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を読み込む、とかにすればいいですね。

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

コメントを残す

*

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

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