WordPressで独自のRSSを作成する

SmartNewsでは規定のフォーマットでRSSを配信すると記事を取り上げて貰える事があります。規定フォーマットに対応後もちろんSmartNews運営に申請が必要で、どのような記事が実際に取り上げてもらえるかは分からないですが。アクセスが欲しいWebメディアは対応しておいて損はないと思います。ちなみにこのブログで対応したわけではないです。

SmartNewsの規定フォーマットについてはこちらです。フォーマットのチェックツールも提供されています。今回はWordPressでSmartNews対応の独自のRSSを作成する方法についてメモしておきます。

SmartNews用のRSSのテンプレートを作成する

下記の内容をfeed-smartnews.phpという名称でテーマのルートに保存します。

<?php

/**
 * SmartNewsのRSS2フォーマットに対応する
 * http://docs.smartnews.com/smartformat/ja/
 */

$copyright = '© ○○株式会社';
$logo = get_stylesheet_directory_uri() . '/img/rss/logo.png'; // ロゴ画像は512px x 512pxを作成する事
$ttl = 15; // 更新頻度(分) 15がMAX
$related_posts_limit_count = 5; // 関連記事の表示限度 5がMAX

// 広告枠の設定を取得する
// テーマオプションで設定されている想定です。適宜書き換えて下さい。
$adcontent = get_option( 'smartnews_adcontent' );

header( 'Content-Type: ' . feed_content_type( 'rss2' ) . '; charset=' . get_option( 'blog_charset' ), true );
echo '<?xml version="1.0" encoding="'.get_option( 'blog_charset' ).'"?'.'>';
?>

<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:media="http://search.yahoo.com/mrss/"
	xmlns:snf="http://www.smartnews.be/snf"
>

	<channel>

		<title><?php bloginfo_rss( 'name' ); wp_title_rss(); ?></title>
		<link><?php bloginfo_rss( 'url' ) ?></link>
		<description><?php bloginfo_rss( 'description' ) ?></description>
		<pubDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', get_lastpostmodified( 'GMT' ), false ); ?></pubDate>
		<language><?php bloginfo_rss( 'language' ); ?></language>
		<copyright><?php echo $copyright; ?></copyright>
		<ttl><?php echo $ttl; ?></ttl>
		<image>
			<url><?php echo $logo; ?></url>
			<title><?php bloginfo_rss( 'name' ); wp_title_rss(); ?></title>
			<link><?php bloginfo_rss( 'url' ); ?></link>
		</image>

<?php
while ( have_posts() ) :
	the_post();
	$content = get_the_content_feed( 'rss2' );
	$image_id = get_post_thumbnail_id();
	$image_url = wp_get_attachment_image_src( $image_id, true );
	$status = 'publish' === $post->post_status ? 'active' : 'deleted';
	$related_posts = get_related_posts( $related_posts_limit_count, $logo );
?>
		<item>
			<title><?php the_title_rss() ?></title>
			<link><?php the_permalink_rss() ?></link>
			<guid><?php comments_link_feed(); ?></guid>
			<description><!&#91;CDATA&#91;<?php the_excerpt_rss(); ?>&#93;&#93;></description>
			<pubDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true ), false ); ?></pubDate>
			<content:encoded><!&#91;CDATA&#91;<?php echo $content; ?>&#93;&#93;></content:encoded>
			<category><?php print_category_rss_smartnews(); ?></category>
			<dc:creator><?php the_author() ?></dc:creator>
			<language><?php bloginfo_rss( 'language' ); ?></language>
			<media:thumbnail><?php echo $image_url&#91;0&#93;; ?></media:thumbnail>
			<media:status><?php echo $status; ?></media:status>
<?php foreach ( $related_posts as $related_post ) : ?>
			<snf:relatedLink title="<?php echo $related_post&#91;'title'&#93; ?>" thumbnail="<?php echo $related_post&#91;'thumbnail'&#93;; ?>" link="<?php echo $related_post&#91;'link'&#93;; ?>" />
<?php endforeach; ?>
<?php if ( ! empty( $adcontent ) ) : ?>
			<snf:advertisement>
				<snf:adcontent><!&#91;CDATA&#91;
					<?php echo $adcontent; ?>
				&#93;&#93;></snf:adcontent>
			</snf:advertisement>
<?php endif; ?>
		</item>
<?php endwhile; ?>

	</channel>
</rss>

<?php

/**
 * カテゴリーをカンマ区切りで出力する
 */
function print_category_rss_smartnews() {
	$categories = get_the_category();
	$cat_names = &#91;&#93;;
	if ( ! empty( $categories ) ) {
		foreach ( (array) $categories as $category ) {
			$cat_names&#91;&#93; = sanitize_term_field( 'name', $category->name, $category->term_id, 'category', 'rss' );
		}
	}
	$cat_names = array_unique( $cat_names );
	echo implode( ',', $cat_names );
}

/**
 * 関連する投稿を取得する
 * @param $related_posts_limit_count
 * @param $noimage_url
 * @return array
 */
function get_related_posts( $related_posts_limit_count, $noimage_url ) {
	global $post;
	$categories = get_the_category( $post->ID );
	$category_ids = [];
	if ( $categories ) {
		$category_ids = array_map( function ( $category ) {
			return $category->term_id;
		}, $categories );
	}
	$args = [
		'category__in' => $category_ids,
		'post__not_in' => [ $post->ID ],
		'showposts' => $related_posts_limit_count,
	];
	$query = new WP_Query( $args );
	$related_posts = [];
	if ( $query->have_posts() ) {
		while ( $query->have_posts() ) {
			$query->the_post();

			$related_image_id = get_post_thumbnail_id();
			$related_image = wp_get_attachment_image_src( $related_image_id, true );

			$related_image_url = $noimage_url;
			if ( $related_image ) {
				$related_image_url = $related_image[0];
			}

			$related_posts[] = [
				'title' => get_the_title(),
				'link' => get_the_permalink(),
				'thumbnail' => $related_image_url,
			];
		}
		wp_reset_postdata();
	}
	return $related_posts;
}

functions.phpにRSSテンプレートを設定する

functions.phpに下記のコードをコピペします。

function custom_feed_smartnews() {
    $template_file = '/feed-smartnews.php';
    load_template( get_template_directory() . $template_file );
}
add_feed( 'smartnews', 'custom_feed_smartnews' );

// フィードのキャッシュを無効に
function prefix_set_feed_cache_time( $seconds ) {
    return 1;
}
add_filter( 'wp_feed_cache_transient_lifetime' , 'prefix_set_feed_cache_time' );

RSSのキャッシュに気をつけましょう

RSSの開発時はキャッシュにハマりやすいです。

コードを更新しても反映されない

上記のコードではwp_feed_cache_transient_lifetimeでキャッシュする時間を1秒にしています。特に開発時はこのコードを入れておかないと一度RSSを表示したら以降はずっとそのキャッシュが効いてしまってデバッグができなくなります。デフォルトだと12時間キャッシュするらしく、しかもキャッシュのクリアの仕方がググってもイマイチよく分かりません。

ブラウザのRSSのキャッシュに注意する

wp_feed_cache_transient_lifetimeでキャッシュを無効にしていてもChromeを使っている場合はShiftキーを押しながら再読み込みしてやらないとブラウザがキャッシュから表示してしまったりします。ここも案外ハマりやすいので注意しましょう。

広告等を動的に表示する場合はキャッシュは無効に

今回のサンプルだとテーマオプションから広告設定を引っ張ってくるようにしているのでキャッシュが効いてしまうと問題があって、wp_feed_cache_transient_lifetimeの設定は本番でも必要になります。

パーマリンク設定の保存をお忘れなく

上記の設定を行っていればパーマリンク設定が投稿名等にしてある場合は/feed/smartnewsというURLでアクセスできるようになります。ただし、1度「パーマリンク設定」の保存を明示的にしてやらないと/feed/smartnewsのURLがWordPressに反映されないようです。何も変更する事がなくても1度パーマリンク設定の保存をしましょう。

注意点

上記のコードはあくまでサンプルです。SmartNewsの規定によると二次配信コンテンツや記事広告については規定のRSSで配信しないように定めがありますので、該当する場合はRSSで表示する記事をフィルターしてあげる必要があります。