【更新履歴】
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です。
目次
概要
特徴
Atom RSS1.0 RSS2.0に対応。
複数のRSSを読み込んで記事を日時の順にソートし記事数を指定して表示できる。
各RSSの読み込む記事数を個別に設定できる。
RSSデータはテキストファイル(json形式)に保存するので頻繁にRSS配信元にアクセスしない。
複数のRSSを読み込めますので、いろんな用途で使えると思います。
内容
サンプルページ
RSSサンプルページ – ウェブソク
機能は大きく分けて2つ
サンプルのファイルリストは上のようになるのですが、重要な機能は大きく分けて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設定 – マニュアル|エックスサーバー
ソース
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[] = 'https://gigazine.net/news/rss_2.0/'; $entry_cnt[] = 5; //Gigazine
$feed[] = 'https://www.gizmodo.jp/index.xml'; $entry_cnt[] = 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[$i] = mb_convert_encoding($response[$i], "utf8", "auto");
$search = array("\0", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x0b", "\x0c", "\x0e", "\x0f");
$response[$i] = str_replace($search, '', $response[$i]);
$rssdata = simplexml_load_string($response[$i], '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[i]) {
continue;
} //記事がない場合は次へ
var title = textSlice(json[i].title[0]);
var url = json[i].url[0];
var date = json[i].date;
var image = json[i].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.html
をindex.php
にして、別途タイムスタンプ記録用のファイルを1つ用意。これと現在時刻を比較して、設定時間以上ならget_rss.php
を読み込む、とかにすればいいですね。いろいろと応用が効いて便利だと思いますので、ぜひ試してみて下さい。