カテゴリー
WordPress

RSS で取得したフィードデータを自動的に WordPress に投稿として登録する方法の勉強メモ♪

テストはあまりしてません。バグなどがあると思います。勉強なのでソースよりもポイントが重要です。以上の注意がありますが、こんな感じの仕様にしました。

  • RSS で取得したフィードデータを自動的に WordPress に投稿として登録します。
  • 1フィード1投稿とします。
  • 投稿タイトルはエントリー投稿日時とし、div タグでくくって、id 属性として、フィードの ID を設定します。
  • 投稿は重複させないものとします。具体的にはフィードを登録する前に、投稿の検索をして、ヒットしたらそのフィードは除外します。
  • 複数種類のフィードを整理できるようにフィードごとにカテゴリーを作成します。
  • 個人的に使うだけ(公開しない)ので、性能は度外視します。

function.php のポイント

RSS を操作する WordPress の関数と SimplePie の関数

  • fetch_feed( $uri )
    この関数は、外部の RSS フィードを取得して解析します。
  • get_item_quantity ( [int $max = 0] )
    フィード中のアイテム数を返します。
    $max 返却する最大アイテム数。
  • set_cache_duration ( [int $seconds = 3600] )
    フィードがキャッシュされる最小時間(単位、秒)を設定します。
    $seconds キャッシュ秒数。
  • init()
    フィード、フィードのパースなどを初期化します。
  • get_item_quantity ( [int $max = 0] )
    フィード中のアイテム数を返します。引数が0の場合は、フィードに含まれる全アイテム数を返します。引数に設定した数字よりもフィード中のアイテム数が多い場合は引数を返します。
    $max 返却最大アイテム数
  • get_items ( [int $start = 0 [, int $length = 0] ] )
    フィードのアイテムを返します。
    $start アイテムの取得位置。0が最初のアイテムになります。
    $length 返却するアイテム数。
  • get_feed ()
    現在のアイテムを返します。
  • get_title ()
    投稿のタイトルを返します。
  • get_date ( [$date_format = ‘j F Y, g:i a’] )
    投稿の日時/タイムスタンプを返します。PHPの date関数の日付フォーマットが使用できます。
    $date_format PHPの date関数の日付フォーマット
  • get_id ( [(bool) $hash = false] )
    投稿のユニークIDを返します。
    $hash true の場合、アイテムのMD5ハッシュを返します。
  • get_permalink()
    オブジェクトにある最初のalt タグのリンクを取得します。
  • get_description ()
    アイテムの詳細を返却します。サマリーをコンテンツよりも優先して返します。もしサマリーない場合はコンテンツを返します。

 WordPress のエラーチェック

WordPress のカテゴリー操作

  • wp_create_category( $cat_name, $parent )
    この関数は、カテゴリー名と(オプションで)親カテゴリーを指定して「シンプルな」カテゴリーを作成します。
    2行目の include_once はwp_create_category関数を使用するために必要です。

    include_once(ABSPATH . 'wp-admin/includes/taxonomy.php');
  • get_category_link( $category_id );
    指定したカテゴリー ID の正しい URL を PHP の値として返す。

WordPress の投稿関数

  • wp_insert_post( $post );
    データベースに投稿を追加します。無害化や値のチェック、デフォルト値の設定なども行います。投稿オブジェクトを引数に取り、作成された投稿の ID(失敗時は0)を返します。
    $post 投稿オブジェクト配列。キーの名前は wp_posts テーブルの各フィールド名と一致させます。

WordPress のデータベース操作

  • $wpdb->prepare( ‘query'[, value_parameter, value_parameter … ] )
    SQL エスケープを行います。SQL を書くときは必ず使用します。
    query 実行したい SQL クエリ。%s (文字列)および %d (整数)がプレースホルダーになる。
    value_parameter プレースホルダーに代入する値。
  • get_var(‘query’,column_offset,row_offset)
    テーブルのひとつのカラムから値を返します。column_offset と row_offset は使ったことありません。気にしなくてよさそうです。
    query 実行したい SQL クエリ。
    column_offset 必要としている列のオフセット (一つ目は 0)。初期値は 0。
    row_offset 必要としている行のオフセット (一つ目は 0)。初期値は 0。
  • $wpdb->get_results(‘query’, output_type)
    SQL を実行します。変数の SELECT 、行の SELECT 、列の SELECT といった制限はありません。
    query 実行したい SQL クエリ。パラメータを null にすると、前回のクエリ結果のキャッシュ中からデータを返す。
    output_type 下の定数のいずれか。初期値は OBJECT。詳細および例については、行の SELECT セクションを参照。
    OBJECT 結果をオブジェクトとして出力。
    ARRAY_A 結果を連想配列として出力。
    ARRAY_N 結果をインデックス配列として出力。

index.php のポイント

  • query_posts($query)
    引数で指定した条件によって WordPress ループ で表示される投稿を変更します。条件で絞られた投稿を返却します。
    $args 組み合わせることで多彩なクエリが可能となります。引数はアンド記号(&)を使って複数組み合わせて指定することができます。
    今回使用した引数を解説します。

    • post_type=post 返却する投稿タイプは、投稿とします。ページなどではなく。
    • orderby=title ソートはタイトルで行います。
    • order=DSC ソート順は降順とします。

作成したソースを載せておきます。

function.php

<?php
include_once(ABSPATH . WPINC . '/feed.php');
include_once(ABSPATH . 'wp-admin/includes/taxonomy.php');

/**
 * RSSフィードをエントリーごとに投稿します。
 * カテゴリーはRSSフィード名で、登録されていなければ自動的に追加します。
 * @param $url RSSフィードのURL
 */
function insert_feed_posts($url) {
	// SimplePie のオブジェクトを取得
	$rss = fetch_feed($url);

	// フィードが生成されていない、または、フィードのエントリーが0の場合は関数終了
	if (is_wp_error($rss) || $rss->get_item_quantity() == 0) {
		return;
	}

	// 30分ごとにキャッシュするよう設定
	$rss->set_cache_duration(1800);
	$rss->init();
	$maxitems = $rss->get_item_quantity(0);
	$rss_items = $rss->get_items(0, $maxitems);
	// タイムゾーンを東京に設定
	date_default_timezone_set('Asia/Tokyo');

	// カテゴリーIDおよびURLを取得(カテゴリーが存在しない場合は新規作成)
	$rss_title = $rss_items[0]->get_feed()->get_title();
	$category_id = get_cat_ID($rss_title);
	if ($category_id == 0) {
		$category_id = wp_create_category($rss_title, 0);
	}
	$category_link = get_category_link($category_id);

	// 投稿を作成
	foreach ($rss_items as $item) {
		// タイトル エントリー投稿日時 + 投稿ID(divでくくる) 投稿の表示順をエントリーの日時でソートするためtitle属性は一番最初に配置する
		$title_date = $item->get_date("Y/m/d H:i:s");
		$post_title = '<div title="'.$title_date.'" id="'.$item->get_id().'" class="entry-date">'.$title_date.'</div>';
		//echo $post_title; // zzz
		// タイトルが既出の場合はスキップ
		if (has_title($post_title)) continue;
		// 本文 1行目 記事画像 + エントリータイトル 2行目 エントリー記事
		$post_content = '
<h2 class="entry-title"><a class="entry-title-link" href="'.$item->get_permalink().'" target="_blank">'.$item->get_title().'</a></h2>
<div class="item-body">'.$item->get_description().'</div>
<div class="entry-author"><span class="entry-source-title-parent"><a class="entry-source-title" href="'.$category_link.'">'.$item->get_feed()->get_title().'</a></span></div>';
		//echo $post_title.'<br />'; // zzz
		//echo $post_content.'<br /><br />'; // zzz

		// 投稿
		$postarr = Array(
			'post_category' => array($category_id),
			'post_content' => $post_content,
			'post_date' => $item->get_date("Y-m-d H:i:s"),
			'post_status' => 'publish',
			'post_title' => $post_title,
		);
		wp_insert_post($postarr);

	}

}

/**
 * 引数の文字列が投稿タイトルに存在しているかどうかを判定します。
 * @param $post_title タイトル文字列
 * @return 投稿タイトルが存在する場合はtrue、存在しない場合はfalse
 */
function has_title($post_title) {
	global $wpdb;
	$sql = $wpdb->prepare("
		SELECT count(*)
		FROM $wpdb->posts
		WHERE post_title = %s
		AND post_status = 'publish'
		", $post_title);
	//echo $sql.'<br />'; // zzz
	$post_title_number = $wpdb->get_var($sql);
	//echo $post_title_number.'<br />'; // zzz
	return $post_title_number > 0 ? true : false;
}
/**
 * テキストが同じタイトルの投稿をマージします。
 */
function merge_post() {
	// 重複投稿のIDを取得(ただし重複している投稿の中で最小IDのものは取得しない)
	global $wpdb;
	$sql ="
		SELECT ID
		FROM $wpdb->posts p1
		WHERE post_status = 'publish'
		AND ID > (
			SELECT MIN(ID)
			FROM $wpdb->posts p2
			WHERE p1.post_title = p2.post_title
		)
		LIMIT 0 , 100
		";
	$posts = $wpdb->get_results($sql);
	//var_dump($posts); // zzz
	echo 'delete start';
	$i =0;
	// 投稿を削除
	foreach ($posts as $post) {
		wp_delete_post($post->ID);
		$i++;
	}
	echo $i;
	echo 'delete end';
}
/* ★☆★ ここからデバッグ用 ★☆★
	// とりあえずフィードをHTML出力
	if ($maxitems == 0) : ?>
		<ul><li>No items.</li></ul>;
	<?php else : ?>
		<h2><?php echo $rss_items[0]->get_feed()->get_title(); ?></h2>
		<ul class="feed-block" >
		<?php foreach ( $rss_items as $item ) : ?>
			<li class="feed-tip" >
				<a href="<?php echo $item->get_permalink(); ?>" target="_blank" title="<?php echo mb_substr(strip_tags($item->get_description()), 0, 200); ?>・・・(<?php echo $item->get_date("Y-n-j H:i:s"); ?>)">
					<?php echo $item->get_title(); ?>
				</a>
			</li>
		<?php endforeach; ?>
		</ul>
	<?php endif;
*/

/*
	// 	タイトル:フィード名、投稿内容:フィード内容、で投稿投稿
	// 投稿内容生成
	$post_title = $rss_items[0]->get_feed()->get_title().' ('.gmdate( 'Y-n-j', (time() + (get_option('gmt_offset') * 3600 ))).')';
	$post_content = '<ul class="feed-block" >';
	foreach ($rss_items as $item) {
		$post_content .=
			'<li id="'.$item->get_id().'" class="feed-tip" >
				<a href="'.$item->get_permalink().'" target="_blank" title="'.mb_substr(strip_tags($item->get_description()), 0, 200).'・・・('.$item->get_date("Y-n-j H:i:s").')">'.$item->get_title().'</a>
			</li>';
	}
	$post_content .= '</ul>';
	$postarr = Array(
		'post_status' => 'publish',
		'post_category' => array(1),
		'post_title' => $post_title,
		'post_content' => $post_content
	);

	// 投稿
	wp_insert_post($postarr);
*/

/*	global $wpdb;
	// 投稿の新規追加・更新
	foreach ($rss_items as $item) {
		// フィードから投稿タイトル候補を生成
		$post_title = $item->get_feed()->get_title().' ('.$item->get_date("Y-n-j").')';
		//echo '<br /><br />投稿タイトル候補 : '.$post_title.'<br />'; // zzz
		// ブログ内の投稿をSELECTするSQL
		$sql = $wpdb->prepare("
			SELECT ID
			FROM $wpdb->posts
			WHERE post_title = '%s'
			AND post_status = 'publish'
			", $post_title);
		//echo $sql; // zzz
		$id = $wpdb->get_var($sql);
		//echo '検索結果<br />';
		// 投稿のタイトルが既出かどうか判定
		if(!is_null($id)) {
			// 投稿の中にエントリーが存在するかどうか判定
			// 指定したIDの投稿中にエントリーが存在するかどうか判定
			$sql = $wpdb->prepare("
				SELECT post_content
				FROM $wpdb->posts
				WHERE ID = %d
				AND post_content like '%s'
				AND post_status = 'publish'
				", $id, "%{$item->get_id()}%");
			//echo $sql.'<br />'; // zzz
			$post_content = $wpdb->get_var($sql);
			// 既出の場合、何もしない
			// 存在しない場合、投稿の末尾にエントリーを追加
			if(is_null($post_content)) {
				// 指定IDの投稿の本文を取得
				$sql = "
					SELECT post_content
					FROM $wpdb->posts
					WHERE ID = $id";
				$post_content = $wpdb->get_var($sql);
				//echo '追加されるもの<br />'; // zzz
				//echo $post_content.'<br />'; // zzz
				// 追加するエントリーのHTMLを生成
				$entry = '
					<li id="'.$item->get_id().'" class="feed-tip" >
						<a href="'.$item->get_permalink().'" target="_blank" title="'.mb_substr(strip_tags($item->get_description()), 0, 200).'・・・('.$item->get_date("Y-n-j H:i:s").')">'.$item->get_title().'</a>
					</li>';
				//echo '追加するよ<br />'; // zzz
				//echo $entry.'<br />'; // zzz
				// 投稿を更新
				// zzz 'ID'は'id'だと正しく動作しない!やられた!
				$postarr = Array(
					'ID' => $id,
					'post_status' => 'publish',
					'post_category' => array(1),
					'post_content' => str_replace('</ul>', $entry.'</ul>', $post_content)
				);
				//echo 'これで更新するよ<br />'; // zzz
				//var_dump($postarr).'<br />'; // zzz
				$id = wp_update_post($postarr);
				//echo '更新後返り血:'.$id.'<br />'; // zzz
			}
		} else {
			//echo '新規に投稿を作成<br />'; // zzz
			// 新規に投稿を作成
			// 投稿内容生成
			$post_content = '
				<ul class="feed-block" >
					<li id="'.$item->get_id().'" class="feed-tip" >
						<a href="'.$item->get_permalink().'" target="_blank" title="'.mb_substr(strip_tags($item->get_description()), 0, 200).'・・・('.$item->get_date("Y-n-j H:i:s").')">'.$item->get_title().'</a>
					</li>
				</ul>';
			$postarr = Array(
				'post_status' => 'publish',
				'post_category' => array(1),
				'post_title' => $post_title,
				'post_content' => $post_content
			);

			// 投稿
			wp_insert_post($postarr);
		}
	}

	// フィードが投稿内容に既出かどうか判定
*/

?>

index.php

<?php get_header(); ?>

<h2>ここから投稿記事</h2>
<?php
	query_posts('post_type=post&orderby=title&order=DSC');
	if (have_posts()) : while (have_posts()) : the_post();
?>

	<?php the_content(); ?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a><br>

<?php
	endwhile; endif;
	wp_reset_query();
?>

<h2>フィード取得</h2>
<?php echo gmdate( 'Y-m-d H:i:s', (time() + (get_option('gmt_offset') * 3600 ))); ?><br />

<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/13762990276854294677'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/1667707234534588298'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/9569980308283064476'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/8970152788881663410'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/3685199190769525989'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/15333522027421652818'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/7067078886846404793'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/5062337057549080239'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/12463644099008496576'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/12181657933223200235'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/11715064283460628734'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/11170405780041752370'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/15945117816102481220'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/16249993188778788321'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/13849019535541414920'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/7137105216888653617'); ?>
<?php insert_feed_posts('http://www.google.com/reader/public/atom/user/13662405869758402078/state/com.google/alerts/9845684636315757987'); ?>
<?php insert_feed_posts('http://search.twitter.com/search.atom?q=HASproject'); ?>
<?php insert_feed_posts('http://search.twitter.com/search.atom?q=HAS%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88'); ?>
<?php insert_feed_posts('http://search.twitter.com/search.atom?q=%E3%82%A2%E3%83%AD%E3%83%8B%E3%82%A2'); ?>
<?php insert_feed_posts('http://search.twitter.com/search.atom?q=%E3%82%B7%E3%83%BC%E3%83%99%E3%83%AA%E3%83%BC'); ?>
<?php insert_feed_posts('http://search.twitter.com/search.atom?q=%E3%83%8F%E3%82%B9%E3%82%AB%E3%83%83%E3%83%97'); ?>
<?php insert_feed_posts('http://search.twitter.com/search.atom?q=%E3%83%99%E3%83%AA%E3%83%BC%E3%83%A9%E3%83%B3%E3%83%89%E3%81%BB%E3%81%A3%E3%81%8B%E3%81%84%E3%81%A9%E3%81%86'); ?>
<?php insert_feed_posts('http://search.twitter.com/search.atom?q=%E3%83%99%E3%83%AA%E3%83%BC%E3%83%A9%E3%83%B3%E3%83%89%E5%8C%97%E6%B5%B7%E9%81%93'); ?>
<?php insert_feed_posts('http://search.twitter.com/search.atom?q=%E3%83%AA%E3%83%88%E3%83%AB%E3%83%99%E3%83%AA%E3%83%BC%E3%82%BA'); ?>

<?php merge_post(); ?>

<?php echo gmdate( 'Y-m-d H:i:s', (time() + (get_option('gmt_offset') * 3600 ))); ?><br />

<?php get_footer(); ?>

header.php

<!DOCTYPE HTML>
<html lang="ja-JP">
<head>
	<meta charset=<?php bloginfo('charset'); ?>>
	<title><?php bloginfo('name'); ?><?php wp_title(' | '); ?></title>
	<link rel="stylesheet" type="text/css" href="<?php bloginfo('stylesheet_url'); ?>" media="all" />
</head>
<body <?php body_class(); ?>>
<div id="wrapper">
<header>
	<h2>ヘッダー</h2>
	<nav>ナビ</nav>
</header>

<div id="content">

footer.php

</div>

<footer>フッター</footer>
</div>
</body>
</html>

style.css

@charset "utf-8";
/*
Theme Name: NEWS
Theme URI: http://localhost/
Description: ニューステーマ
Author: 管理者
Version: 1.0
Tags: tag1, tag2
*/

おわりに。参考にさせていただいたウェブページ♪感謝いたします。

RSS の扱い方

  • 関数リファレンス/fetch feed – WordPress Codex 日本語版
    WordPressにはRSS関係の関数はないと思い込んでいたのですが、ぜんぜんそんなことありませんでした。とはいえ、ゼロから実装しているわけではなく、オープンソースのSimplePieというものを使っているそうです。使い方も載っているので、とても参考になります。
  • SimplePie Documentation: API Reference
    WordPressのページは簡単なことしか載っていないので、こちらの本家サイトも覗きます。

投稿を操作する方法

データベースの操作方法。検索をするのに

カテゴリーを操作する方法

投稿表示順番のカスタマイズ方法

PHPで気になったこと。日付のフォーマットの指定方法など。

  • PHP: 論理演算子 – Manual
    // $e に代入されるのは、(false || true) の評価結果です
    // これは、次の式と同様です: ($e = (false || true))
    $e = false || true;
    // $f には false が代入され、true は無視されます
  • PHP: date – Manual
    m = 月。数字。先頭にゼロをつける。01 から 12
    d = 日。二桁の数字(先頭にゼロがつく場合も)01 から 31

「RSS で取得したフィードデータを自動的に WordPress に投稿として登録する方法の勉強メモ♪」への1件の返信

こんにちは、アンテナサイトを作成するために、fetch feedで取得したRSS情報を、投稿したいと思っています。ただ、記事の内容まで保存したくはありませんし、タイトルも保存する必要はありません。

アンテナサイトを作成する場合、他のサイトにhttp://antenna.jp/feed/などのRSSを張ってもらうわけですが、アンテナサイト上でRSSの更新があった場合、他のサイトに張ってもらっているRSSも更新されるように設定できないでしょうか。

ご存知であれば、教えていただけるとありがたいです。