当ページのリンクには一部広告が含まれています。
うさねこ気まぐれPG開発室

【WordPress・Cocoon】複数サイトの更新をカード型RSSリーダー風に表示する作り方【ソースあり】

※ 外部RSSの仕様変更や配信制限により、記事が取得できない場合があります。原因は外部要因/設定/本プログラムのいずれの場合もあるため、まずはRSS URLと表示状況をご確認ください。

うさ、作ってみたよ!

うさちゃん
うさちゃん

ねこちゃん! ついに…ついにできたよ!!✨
うさねこ散歩の記事と、FF14のニュースサイト、それに他のブログのRSSも全部まとめて表示できる“RSSまとめPG”を作っちゃったの…!🐰💻

ねこちゃん
ねこちゃん

なんかよくわかんないけど、なんかすごそう!!!

うさちゃん
うさちゃん

自分でいうのもなんだけど、すごく便利だよ~!
デザインをカード型にして、サムネイルも自動で取ってきて、最新順に並べて…
しかも、うさねこ散歩の記事だけは、仮にアイキャッチがない時も “ふわっと可愛い固定画像” を出すようにしたの🐰💞

ねこちゃん
ねこちゃん

なんかよくわかんないけど、見たい記事が“情報ウォール”として視認できるRSSのプログラムをうさちゃんが作ってくれました!

カード型RSSリーダー風「UsaNeko RSS Wall」とは?

最大5つのRSSフィードを読み込み、
最新記事をカード形式でおしゃれに表示するショートコード です。

  • 複数ドメインのRSSを一つにまとめて表示
  • 最新順に自動ソート
  • タイトル・説明文・日付・サイト名をカード化
  • サムネイル(アイキャッチ)を自動で取得
  • 指定ドメインのRSSだけ、アイキャッチが取れなかった場合に補完画像を自動表示

🔧 注意:記事中の「usaneko_usaneko_sample.com」ドメインはサンプルです。
コード内の usaneko_usaneko_sample.comあなたのドメインに変更
例:https://.../uploads/...png補完アイキャッチの実URLに変更
ショートコードの feed1〜feed5取得したいRSS URLに変更


🔧 ファイルを置く場所

PHPのプログラムは以下へ設置している想定:

例:/wp-content/themes/子テーマ/ に設置

Cocoon子テーマの functions.php から読み込んであれば、このまま動きます。


ショートコードの使い方

基本形

[usaneko_rss
  feed1="https://example.com/feed/"
  feed2="https://example2.com/feed/"
  feed3=""
  feed4=""
  feed5=""
  limit="10"
  per_feed="5"
  title="うさねこRSSピックアップ"
]

ショートコード引数一覧

引数内容
feed1feed5取得したいRSSフィードURL(空欄は無視)
limit全体で最大何件表示するか(デフォルト 10)
per_feed1つのRSSから何件取るか(デフォルト 5)
title上部の見出しタイトル

使用例①:1つのRSSだけ表示

[usaneko_rss
  feed1="https://example.com/feed/"
  limit="8"
  per_feed="8"
  title="最新記事"
]

使用例②:複数RSSをまとめる

[usaneko_rss
  feed1="https://example.com/feed/"
  feed2="https://blog.example.net/feed/"
  feed3="https://media.example.org/feed/"
  limit="12"
  per_feed="5"
  title="最新記事まとめ"
]

プログラムの内部処理をやさしく解説


① CSSの読み込み

プログラム内では、外部CSSファイルを使わず
PHP側でインラインCSSを生成して読み込むスタイルになっています。

wp_register_style( 'usaneko-rss-wall', false );
wp_enqueue_style( 'usaneko-rss-wall' );
wp_add_inline_style( 'usaneko-rss-wall', $css );

これで、記事に貼るだけで自動的にデザインが適用されます。


② RSSをまとめて収集する仕組み

心臓部はこちら👇

usaneko_rss_wall_collect_items()

この関数で次の処理をしています。

  1. feed1〜feed5 に入っているURLを順番に読み込む
  2. SimplePie(WordPress標準RSSライブラリ)で記事を取得
  3. タイトル/リンク/日付/説明/サムネイルをまとめる
  4. 全RSSの結果を合体
  5. 日付の新しい順にソート
  6. limit件に絞って返す

③ HTMLタグやコードをすべて除去する処理

RSS本文から不要なタグ(<p><span> など)はすべて取り除いています。

$decoded  = html_entity_decode( $raw_desc, ENT_QUOTES, 'UTF-8' );
$stripped = wp_strip_all_tags( $decoded, true );
$desc     = preg_replace( '/\s+/', ' ', $stripped );
$desc     = trim( $desc );

✔ コードが表示されない
✔ HTMLタグが残らない
✔ 改行やタブもキレイに整形される

記事一覧として見やすい文章になるよう最適化されています。


④ サムネイル(アイキャッチ)画像の取得

次の優先順で画像URLを探しています:

  1. RSSの <enclosure> 画像
  2. SimplePieの get_thumbnail()
  3. 本文内の <img src="">
  4. 指定ドメインだけは固定画像で補完

プログラム内の該当部分:

$is_usaneko_feed = in_array(
    $host,
    [ 'usaneko_usaneko_sample.com', 'www.usaneko_usaneko_sample.com' ], // ←サンプル
    true
);

if ( ! $thumb && $is_usaneko_feed ) {
    $thumb = $l_StrUsanekoFallbackThumb;
}

⚠️ ここは必ずご自身のドメインに置き換えてください。
usaneko_usaneko_sample.com」はサンプルです。


⑤ 仕上げ:ショートコードがHTMLとして出力

最終的に、かわいいうさねこカードとして記事を描画します。

  • サムネイル
  • サイト名
  • タイトル
  • 日付
  • 説明文

が整ったレイアウトで並びます。

ソース

<?php
/**
 * 🐰 UsaNeko RSS Wall
 * Path: /wp-content/themes/あなたの子テーマ/usaneko_rss_wall.php
 *
 * Shortcode:
 *   [usaneko_rss
 *      feed1="https://usaneko_usaneko_sample.com/feed/"
 *      feed2="https://example.com/feed/"
 *      feed3=""
 *      feed4=""
 *      feed5=""
 *      limit="10"
 *      per_feed="5"
 *      title="うさねこRSSピックアップ"
 *   ]
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * CSS 登録(ハンドルのみ)
 */
add_action( 'wp_enqueue_scripts', function () {
    // ダミースタイルを登録(後で inline CSS を乗せる)
    if ( ! wp_style_is( 'usaneko-rss-wall', 'registered' ) ) {
        wp_register_style( 'usaneko-rss-wall', false );
    }

    // すでに CSS を追加済みなら何もしない
    static $usaneko_rss_wall_css_done = false;
    if ( $usaneko_rss_wall_css_done ) {
        return;
    }
    $usaneko_rss_wall_css_done = true;

    $css = <<<CSS
/* 🐰 UsaNeko RSS Wall */
.usaneko-rss-wall {
  margin: 2em 0;
  padding: 1.5em 1.8em;
  border-radius: 1.2em;
  background: linear-gradient(135deg, #f9fffb, #f5fbff);
  border: 1px solid rgba(180, 210, 200, .7);
  box-shadow: 0 10px 25px rgba(0, 0, 0, .04);
  font-size: .95rem;
}
.usaneko-rss-wall-header {
  display: flex;
  align-items: center;
  gap: .6em;
  margin-bottom: 1.2em;
}
.usaneko-rss-wall-header-icon {
  width: 2.1em;
  height: 2.1em;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  box-shadow: 0 4px 12px rgba(0, 0, 0, .05);
  font-size: 1.3em;
}
.usaneko-rss-wall-header-text-main {
  font-weight: 700;
  letter-spacing: .03em;
  color: #355048;
}
.usaneko-rss-wall-header-text-sub {
  font-size: .82em;
  color: #7a8e87;
}
.usaneko-rss-wall-items {
  display: flex;
  flex-direction: column;
  gap: .9em;
}
.usaneko-rss-card {
  position: relative;
  padding: .9em 1em .95em;
  border-radius: .9em;
  background: #ffffff;
  border: 1px solid rgba(210, 225, 220, .9);
  box-shadow: 0 4px 12px rgba(0, 0, 0, .02);
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
  overflow: hidden;
}
.usaneko-rss-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, .06);
  border-color: rgba(150, 205, 190, 1);
}
.usaneko-rss-card-thumb {
  margin: 0 0 .7em;
  border-radius: .75em;
  overflow: hidden;
  background: #f3f5f4;
}
.usaneko-rss-card-thumb img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 220px;
  object-fit: cover;
}
.usaneko-rss-card-source {
  display: inline-flex;
  align-items: center;
  gap: .3em;
  font-size: .78em;
  padding: .15em .55em;
  border-radius: 999px;
  background: #f3fbf7;
  color: #4c7164;
  margin-bottom: .45em;
}
.usaneko-rss-card-source-label {
  font-weight: 600;
}
.usaneko-rss-card-title {
  font-size: 1em;
  font-weight: 600;
  margin-bottom: .4em;
}
.usaneko-rss-card-title a {
  text-decoration: none;
  color: #263732;
}
.usaneko-rss-card-title a:hover {
  text-decoration: underline;
}
.usaneko-rss-card-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .4em;
  font-size: .78em;
  color: #8a9994;
  margin-bottom: .4em;
}
.usaneko-rss-card-date {
  white-space: nowrap;
}
.usaneko-rss-card-desc {
  font-size: .86em;
  color: #56615e;
  line-height: 1.6;
}
.usaneko-rss-wall-empty {
  font-size: .9em;
  color: #7c8b84;
  text-align: center;
  padding: .8em .2em .2em;
}
@media (max-width: 599px) {
  .usaneko-rss-wall {
    padding: 1.2em 1.2em;
  }
}
CSS;

    wp_enqueue_style( 'usaneko-rss-wall' );
    wp_add_inline_style( 'usaneko-rss-wall', $css );
});

/**
 * RSS を複数ドメインから取得してまとめる
 *
 * @param string[] $feed_urls
 * @param int      $per_feed     1フィードあたりの最大件数
 * @param int      $total_limit  全体の最大件数
 *
 * @return array<int,array{
 *   title:string,
 *   link:string,
 *   date_ts:int,
 *   date_text:string,
 *   source:string,
 *   desc:string,
 *   thumb:string
 * }>
 */
function usaneko_rss_wall_collect_items( array $feed_urls, $per_feed = 5, $total_limit = 10 ) {
    $items_all = [];

    if ( empty( $feed_urls ) ) {
        return $items_all;
    }

    if ( ! function_exists( 'fetch_feed' ) ) {
        include_once ABSPATH . WPINC . '/feed.php';
    }

    // うさねこ固定アイキャッチ(★サンプル:自分の画像URLに変更してください)
    $l_StrUsanekoFallbackThumb = '例:https://.../uploads/...png';

    foreach ( $feed_urls as $url ) {
        $url = trim( $url );
        if ( $url === '' ) {
            continue;
        }

        $host = wp_parse_url( $url, PHP_URL_HOST );
        $is_usaneko_feed = in_array(
            $host,
            [ 'usaneko_usaneko_sample.com', 'www.usaneko_usaneko_sample.com' ],
            true
        );

        $feed = fetch_feed( $url );

        // エラーのフィードは無視
        if ( is_wp_error( $feed ) ) {
            continue;
        }

        // キャッシュ時間(多少短め)
        $feed->set_cache_duration( 1800 );

        $max_items = $feed->get_item_quantity( $per_feed );
        if ( $max_items <= 0 ) {
            continue;
        }

        $feed_items   = $feed->get_items( 0, $max_items );
        $source_title = $feed->get_title();
        if ( ! is_string( $source_title ) || $source_title === '' ) {
            $source_title = $host ? $host : 'RSS';
        }

        foreach ( $feed_items as $item ) {
            /** @var SimplePie_Item $item */
            $title = $item->get_title();
            $link  = $item->get_link();

            if ( ! $title || ! $link ) {
                continue;
            }

            $timestamp = $item->get_date( 'U' );
            if ( ! $timestamp ) {
                $timestamp = time();
            }

            $date_text = date_i18n( 'Y-m-d', $timestamp );

            // ▼▼▼ ここが「HTMLやコードを出さない」ための処理 ▼▼▼
            $raw_desc = $item->get_description();
            if ( ! is_string( $raw_desc ) || $raw_desc === '' ) {
                $raw_desc = $item->get_content();
            }

            if ( ! is_string( $raw_desc ) ) {
                $raw_desc = '';
            }

            // HTMLエンティティをデコードしてからタグを全部剥がす
            $decoded  = html_entity_decode( $raw_desc, ENT_QUOTES, 'UTF-8' );
            $stripped = wp_strip_all_tags( $decoded, true );

            // 改行やタブをスペースにまとめて読みやすくする
            $desc = preg_replace( '/\s+/', ' ', $stripped );
            $desc = trim( $desc );
            // ▲▲▲ ここまで ▼▼▼

            // 🐰 アイキャッチURLを取得
            $thumb = '';

            // 1) enclosure(画像添付)優先
            $enclosure = $item->get_enclosure();
            if ( $enclosure && $enclosure->get_link() ) {
                $thumb = $enclosure->get_link();
            }

            // 2) SimplePie の get_thumbnail() があれば使う
            if ( ! $thumb && method_exists( $item, 'get_thumbnail' ) ) {
                $thumb_candidate = $item->get_thumbnail();
                if ( is_string( $thumb_candidate ) && $thumb_candidate !== '' ) {
                    $thumb = $thumb_candidate;
                }
            }

            // 3) 本文 or コンテンツ中の最初の <img> を拾う
            if ( ! $thumb ) {
                $content = $item->get_content();
                if ( is_string( $content ) && $content !== '' ) {
                    if ( preg_match( '/<img[^>]+src=["\']([^"\']+)["\']/i', $content, $m ) ) {
                        $thumb = $m[1];
                    }
                }
            }

            // 4) サンプルドメイン(うさねこサイト相当)だけ、アイキャッチが取れない場合に固定画像を使用
            if ( ! $thumb && $is_usaneko_feed ) {
                $thumb = $l_StrUsanekoFallbackThumb;
            }

            $items_all[] = [
                'title'     => $title,
                'link'      => $link,
                'date_ts'   => (int) $timestamp,
                'date_text' => $date_text,
                'source'    => $source_title,
                'desc'      => $desc,
                'thumb'     => $thumb,
            ];
        }
    }

    if ( empty( $items_all ) ) {
        return $items_all;
    }

    // 日付の新しい順にソート
    usort(
        $items_all,
        function ( $a, $b ) {
            return $b['date_ts'] <=> $a['date_ts'];
        }
    );

    if ( $total_limit > 0 && count( $items_all ) > $total_limit ) {
        $items_all = array_slice( $items_all, 0, $total_limit );
    }

    return $items_all;
}

/**
 * ショートコード本体
 */
add_shortcode( 'usaneko_rss', function ( $atts ) {
    // デフォルト
    $atts = shortcode_atts(
        [
            'feed1'    => '',
            'feed2'    => '',
            'feed3'    => '',
            'feed4'    => '',
            'feed5'    => '',
            'limit'    => 10, // 全体の最大表示件数
            'per_feed' => 5,  // 1フィードあたりの最大件数
            'title'    => 'うさねこRSSピックアップ',
        ],
        $atts,
        'usaneko_rss'
    );

    // CSS を読み込み
    wp_enqueue_style( 'usaneko-rss-wall' );

    $feed_urls = [];
    for ( $i = 1; $i <= 5; $i++ ) {
        $key = 'feed' . $i;
        if ( ! empty( $atts[ $key ] ) ) {
            $feed_urls[] = $atts[ $key ];
        }
    }

    $limit    = (int) $atts['limit'];
    $per_feed = (int) $atts['per_feed'];

    if ( $limit <= 0 ) {
        $limit = 10;
    }
    if ( $per_feed <= 0 ) {
        $per_feed = 5;
    }

    $items = usaneko_rss_wall_collect_items( $feed_urls, $per_feed, $limit );

    ob_start();
    ?>
    <div class="usaneko-rss-wall">
        <div class="usaneko-rss-wall-header">
            <div class="usaneko-rss-wall-header-icon" aria-hidden="true">
                🐰🐱
            </div>
            <div>
                <div class="usaneko-rss-wall-header-text-main">
                    <?php echo esc_html( $atts['title'] ); ?>
                </div>
                <div class="usaneko-rss-wall-header-text-sub">
                    うさねこが集めてきた最新記事だよ♪
                </div>
            </div>
        </div>

        <?php if ( empty( $items ) ) : ?>
            <div class="usaneko-rss-wall-empty">
                現在表示できるRSS記事がありません。時間をおいて再度お試しください🐰
            </div>
        <?php else : ?>
            <div class="usaneko-rss-wall-items">
                <?php foreach ( $items as $item ) : ?>
                    <article class="usaneko-rss-card">
                        <?php if ( ! empty( $item['thumb'] ) ) : ?>
                            <figure class="usaneko-rss-card-thumb">
                                <a href="<?php echo esc_url( $item['link'] ); ?>" target="_blank" rel="noopener">
                                    <img src="<?php echo esc_url( $item['thumb'] ); ?>" alt="" loading="lazy" decoding="async">
                                </a>
                            </figure>
                        <?php endif; ?>

                        <div class="usaneko-rss-card-source">
                            <span class="usaneko-rss-card-source-label">from</span>
                            <span><?php echo esc_html( $item['source'] ); ?></span>
                        </div>
                        <h3 class="usaneko-rss-card-title">
                            <a href="<?php echo esc_url( $item['link'] ); ?>" target="_blank" rel="noopener">
                                <?php echo esc_html( $item['title'] ); ?>
                            </a>
                        </h3>
                        <div class="usaneko-rss-card-meta">
                            <span class="usaneko-rss-card-date"><?php echo esc_html( $item['date_text'] ); ?></span>
                            <span class="usaneko-rss-card-meta-usaneko">🐾</span>
                        </div>
                        <?php if ( $item['desc'] !== '' ) : ?>
                            <p class="usaneko-rss-card-desc">
                                <?php echo esc_html( $item['desc'] ); ?>
                            </p>
                        <?php endif; ?>
                    </article>
                <?php endforeach; ?>
            </div>
        <?php endif; ?>
    </div>
    <?php

    return ob_get_clean();
});

動作の注意点まとめ

  • RSSの更新が反映されるまで時間がかかる場合があります(目安:数分〜30分程度(環境や配信元により前後します))。
    ※RSSキャッシュは環境や配信元の設定により前後します。すぐ反映したい場合は、WordPress側のRSSキャッシュ(トランジェント)を削除すると改善することがあります。
  • 他サイトのRSSはサムネイルが取れないことがあります
  • 固定アイキャッチが使われるのは 指定ドメインのRSSだけ です
  • ソース内の「サンプルドメイン」と、ショートコードのRSS URLはご自身の環境に合わせて変更してください

※外部RSS配信元の応答が遅い場合、表示に時間がかかることがあります。
※RSSの反映時間は環境や配信元のキャッシュ設定により前後します(目安:数分〜30分程度)


さいごに

うさちゃん
うさちゃん

これで、うさねこ散歩の記事も、FF14ニュースも、他サイトさんの記事も、ぜんぶ一緒に見てもらえるようになった〜🐰✨

ねこちゃん
ねこちゃん

なんかよくわかんないけど、RSSのURLをちょこっと変えるだけで、好きな組み合わせの自分用の“情報ウォール”が作れるって便利! うさちゃんすごい🐱📚

WordPress+Cocoonを始めるなら「エックスサーバー」がおすすめ!

「これからWordPress+Cocoonでブログを作りたい!」という方には、
国内シェアNo.1の高速レンタルサーバー エックスサーバーがぴったりです。

  • 🚀 WordPressクイックスタート対応(最短10分でブログ開設)
  • 🪄 Cocoonテーマの自動インストールにも対応
  • 🔒 無料独自SSL/高セキュリティ/自動バックアップ完備
  • ☁️ 高速SSD・安定稼働率99.99%以上
  • 💰️ スタンダード 月額料金 990円~  693円~(キャンペーン中なら) 2025/11現在

「WordPress × Cocoon」を最もスムーズに始められる環境が整っています。
👉 今すぐチェック:レンタルサーバー エックスサーバー

最新のサービス価格は公式サイトでご確認ください。

免責事項

本記事のサンプルプログラムは動作・結果を保証するものではありません。 利用により発生したいかなるトラブル・損害についても、当方は責任を負いません。

広告