PHPでアメーバブログのRSSから記事と記事内の画像を表示する
目次
運営中のサイトとは別にブログを書いていて、その情報をサイトのTOPへ載せたい、 といった要望があがることがあります。
そのブログサービスが提供するRSSを取得して表示する、というのが簡単で一般的かと思います。
以前、アメーバブログのRSSを取得して情報を載せるところまでは問題なかったのですが、記事の中にある最初の画像をサムネイルとして出したい!といった時に詰まったので、RSSの取得から画像を表示するところまでの工程を共有したいと思います。
注意点
掲載しているソースコードにある、正規表現の一部がWPでの投稿時消えてしまっている箇所があり、そのままではうまく動かない場合があります。
正しいコードは、サンプルコードをご確認下さい。
まずは動作確認から
サンプルブログの記事を取得して、最新の記事を画像付きで表示する、といったものとなっています。
実装の流れ
実装は下記のような流れになります。
- ブログのRSSを取得
- 記事の内容から最初の画像を抜粋
- 画像を
<img>
タグとして表示する
というシンプルなものですが、アメーバブログの画像をそのまま<img>
タグとして出力すると、何かの制限がかかっているらしく、画像が壊れた様な表示になってしまいます。
なんじゃこりゃっ! と色々試していると、同じ画像でもブラウザで直接URLを叩くと画像が表示されることがわかったので、PHPのget_flie_contents()
などでリソースを取得すれば…、と思い試した結果表示が出来ました。
ブログのRSSを取得
まずは実装したメインの関数から。
/**
* get_ameba_rss
* アメーバブログから指定件数のRSS情報を取得します
* @param string $url
* @param integer $limit 全て取得する場合は「-1」
* @return array SimpleXMLElement
*/
function get_ameba_rss( $url, $limit=5 )
{
$rss = curl_get_contents($url);
$rss = simplexml_load_string($rss);
$results = array();
foreach( $rss->channel->item as $item ){
// PR: から始まる広告、指定数以上の記事は除く
if( preg_match("/^PR:.+/",$item->title) || ($limit >= 0 && count($results) >= $limit) ){
continue;
}
// 画像がなかった場合のデフォルト画像を指定しておきます
$now_url = (empty($_SERVER["HTTPS"]) ? "http://" : "https://") . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
$item->thumbnail = trim($now_url,"/") . "/images/noimage.jpg";
// 記事の中で最初に使われている画像を検索、設定する
if( preg_match_all('/<img(.+?)>/is', $item->description, $matches) ){
foreach( $matches[0] as $img ){
if( preg_match('/src=[\'"](.+?jpe?g)[\'"]/', $img, $m) ){
$item->thumbnail = $m[1];
}
}
}
// レスポンス用のPHPへのパスへ設定する
$data = array("f" => (string)$item->thumbnail);
$item->thumbnail = sprintf("response.php?%s", http_build_query($data));
$results[] = $item;
}
return $results;
}
RSSを取得する時に使用するsimplexml_load_file()ですが、エラーが出てしまい使えませんでしたので、一度file_get_contents()で文字列として取得、XMLへ変換するという方法で実装しました。
ほとんどの場合ブログRSSへのパスはhttp://
から始まるかと思います。単純にget_file_contents()
を使ってしまうと「allow_url_fopen」の設定がOffになっているとエラーが出てしまいます。(セキュリティ的にallow_url_fopenはOffにするのが推奨されます。)
なので、cUrlを使った下記の関数を代替として使用しました。
/**
* curl_get_contents
* file_get_contentsの代替関数。
* allow_url_fopen=off時にURLからファイル内容を取得する際に使用します
* @param string $url
* @param integer $timeout
* @return string
* @link http://blog.mach3.jp/2010/12/21/use-curl-for-filegetcontents.html
*/
function curl_get_contents( $url, $timeout=60 )
{
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_HEADER, false );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout );
$result = curl_exec( $ch );
curl_close( $ch );
return $result;
}
広告は表示させない
RSSを取得すると、記事タイトルが「PR:」で始まる広告が入ってきます。これは記事の一覧を表示する時に不要なので取得結果から除外します。
// PR: から始まる広告、指定数以上の記事は除く
if( preg_match("/^PR:.+/",$item->title) || ($limit >= 0 && count($results) >= $limit) ){
continue;
}
記事中の最初の画像を取得、表示用のPHPへ値を渡す
直接<img>
タグを出力しても制限がかかっているのか表示されなかったので、レスポンスを返すPHPを用意して、そこへ画像ファイルのパスを値として渡すようにしています。
// 画像がなかった場合のデフォルト画像を指定しておきます
$now_url = (empty($_SERVER["HTTPS"]) ? "http://" : "https://") . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
$item->thumbnail = trim($now_url,"/") . "/images/noimage.jpg";
// 記事の中で最初に使われている画像を検索、設定する
if( preg_match_all('/<img(.+?)>/is', $item->description, $matches) ){
foreach( $matches[0] as $img ){
if( preg_match('/src=[\'"](.+?jpe?g)[\'"]/', $img, $m) ){
$item->thumbnail = $m[1];
}
}
}
// レスポンス用のPHPへのパスへ設定する
$data = array("f" => (string)$item->thumbnail);
$item->thumbnail = sprintf("response.php?%s", http_build_query($data));
レスポンス用のPHPを用意
上記で渡ってきた画像ファイルのパスから、画像のレスポンスを返すためのphpを別途用意します。
response.php
$f = isset($_GET["f"]) ? $_GET["f"] : "";
$file_uri = str_replace("\0", "", $f);
// 値を取得
if( $file = curl_get_contents($file_uri) ){
$mime = get_mime_type($file_uri);
header("Content-Type: {$mime}");
print $file;
exit();
}
print "Error";
exit();
取得した情報をHTMLへ出力する
ここまでくれば、最初に書いたget_ameba_rss()
を使って情報を取得して、配列を回してHTMLを出力していきます。
<div class="rss-list">
<?php
// RSS情報を取得する
$rss = get_ameba_rss("http://feedblog.ameba.jp/rss/ameblo/webdesign-dackel/rss20.xml");
foreach( $rss as $item ) : ?>
<a href="<?php print $item->link; ?>" target="_blank">
<p class="rss-image"><img src="<?php print $item->thumbnail; ?>" alt="<?php print $item->title; ?>"></p>
<p class="rss-title"><?php print $item->title; ?></p>
</a>
<?php endforeach; ?>
</div>
ここまでで、サンプルと同じ動作になっています。なんかごちゃごちゃと書いてしまい分かりづらい箇所があるかと思いましたのでサンプルと、サンプルファイル一式をダウンロード出来るようにしました。
ところどころ説明を省いた箇所なんかもありますが、分かりづらい箇所はサンプルファイルを追ってみると良いかもです。
ファイルを一度取得して、書き出し直すという泥臭いことをやっているため、なんだかスッキリとしませんがとりあえず上記の方法で実現が出来たので今回はよしとしました。
ブログを読んでいただいた方に、 他にもっと良い方法をご存知の方がいたら是非ご教授頂けたら思いますっ!
更新履歴
追記:2015年1月17日
冒頭に正規表現部分について、注意書きを追加しました。
追記:2014年9月29日
Wodpressでの組み込んだ際に表示が出来ないよ、とのコメントを頂きましたので、Wordpressへ組み込む際の変更箇所について追記いたします。
get_ameba_rss()
get_ameba_rss()
の中で指定している、noimage.jpg
とresponse.php
へのパスをWP用に変更することで解決できます。
/**
* get_ameba_rss
* アメーバブログから指定件数のRSS情報を取得します
* @param string $url
* @param integer $limit 全て取得する場合は「-1」
* @return array SimpleXMLElement
*/
function get_ameba_rss( $url, $limit=5 )
{
$rss = curl_get_contents($url);
$rss = simplexml_load_string($rss);
$results = array();
foreach( $rss->channel->item as $item ){
// PR: から始まる広告、指定数以上の記事は除く
if( preg_match("/^PR:.+/",$item->title) || ($limit >= 0 && count($results) >= $limit) ){
continue;
}
// ↓ noimage.jpgへのパスを変更
// $now_url = (empty($_SERVER["HTTPS"]) ? "http://" : "https://") . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
// $item->thumbnail = trim($now_url,"/") . "/images/noimage.jpg";
$item->thumbnail = get_bloginfo("template_url") . "/images/noimage.jpg";
// 記事の中で最初に使われている画像を検索、設定する
if( preg_match_all('/<img(.+?)>/is', $item->description, $matches) ){
foreach( $matches[0] as $img ){
if( preg_match('/src=[\'"](.+?jpe?g)[\'"]/', $img, $m) ){
$item->thumbnail = $m[1];
}
}
}
// レスポンス用のPHPへのパスへ設定する
$data = array("f" => (string)$item->thumbnail);
// ↓ response.phpへのパスを変更
// $item->thumbnail = sprintf("%s/response.php?%s", get_bloginfo("template_url"), http_build_query($data));
$item->thumbnail = sprintf("%s/response.php?%s", get_bloginfo("template_url"), http_build_query($data));
$results[] = $item;
}
return $results;
}
response.php
あともう一点、response.php
の最上部でfunctions.php
を読み込んでいる箇所を下記のようにwp-load.php
を読み込むように変更します。
// include_once(dirname(__FILE__)."/functions.php");
include_once(dirname(__FILE__) . "/../../../wp-load.php");
例としてあげていますが、お使いのディレクトリ構成に合わせてパスを調整してください。
ちなみに、wp-load.php
はWordPressの一番上の階層、wp-config.php
などと同じ階層にあります。
追記:2015年5月29日
コメントよりご指摘いただき、response.php
内でwp-load.php
を読み込む必要がありましたので記事を修正しました。
追記:2015年10月16日
コメントよりご指摘いただいた、記事中の最初の画像を取得する部分を修正しています。