Now On Air

以前、@miyabi_s さんに、FM京都のオンエア曲を Twitter に自動投稿する仕組みは無いものかと聞かれて、「オンエア楽曲検索を、スクレイピングして RSS フィードを作ってやればいいんじゃね?」と答えたことがありました。
ちゃちゃっと PHP でサンプルプログラムを作って渡したんですが、そのプログラムを利用して FM-NIIGATAFM PORTZIP-FM のオンエア曲を提供する bot を作成したので、ご紹介。

今回、作成したのは以下の4つのアカウントです。

動作原理を簡単に説明すると

  1. 各FM局公式サイトの「オンエア楽曲検索」をスクレイピングしてRSSフィードを作成
  2. cron で定期的にRSSフィードを確認して、新しい情報があれば Twitter に投稿

となります。

自前のサーバで cron 実行せずに twitterfeed.com などの外部サービスを利用したい時、移行が簡単になるようにわざわざRSSフィードを作成しています。
さて、ではプログラムの簡単な説明。

スクレイピングしてRSSフィードを作成

スクレイピング後、RSSフィードを作成するプログラムの骨組みを提示します。

<?php
// ライブラリをインクルード
require( 'Snoopy.class.php' );		// Snoopy
require( 'simple_html_dom.php' );	// PHP Simple HTML DOM Parser

// 定数の設定
define( 'ONAIR_URL',  'http://example.com/OnAirList.asp' );
define( 'RSS_LINK',   'http://example.com/index.asp' );
define( 'SITE_TITLE', 'FM HogeHoge Now On Air' );
define( 'ENCODING',   'UTF-8' ):
define( 'LANGUAGE',   'ja' );

// 変数の初期化
$lastpost = 0;
$posts = array();

// 最新の楽曲放送状況を取得
$snoopy = new Snoopy;
$snoopy->read_timeout = 5;
$snoopy->timed_out = true;
$snoopy->fetch( ONAIR_URL );
$response  = $snoopy->results;
$http_code = $snoopy->response_code;
unset($snoopy);
if ( strpos($http_code, '200') === FALSE ? ) die();

// 取得した HTML の解析
$html_txt = mb_convert_encoding( $response, ENCODING, 'SJIS-WIN, SJIS, EUCJP-WIN, EUCJP, JIS' );
$dom = str_get_html($html_txt);
if ($dom !== FALSE) {
	$elements = (array) $dom->find('table tbody tr');
	$item_count = 0; 
	foreach ($elements as $element) {
		/* 楽曲のタイトル、アーティスト、放送時間などを解析 */
			:
			:
		/* サイトの HTML 構造により、異なるので割愛 */

		$post = array(
			  'title' => $title
			, 'permalink' => $url
			, 'postdate' => $postdate
			, 'description' => $description
			, 'tags' => $tags
			);
		if ( array_search( $post, $posts ) === FALSE )
			$posts[] = $post;
	}
	unset($element); unset($elements);
}
unset($dom);

// RSSフィールドを書き出す
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $lastpost).' GMT');
header('Content-Type: text/xml; charset=' . ENCODING, true);
echo '<?xml version="1.0" encoding="' . ENCODING . '"?' .">\n";
?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" version="2.0">

<channel>
	<title><?php echo SITE_TITLE; ?></title>
	<link><?php echo RSS_LINK; ?></link>
	<description><?php echo SITE_TITLE; ?></description>
	<pubDate><?php echo date('D, d M Y H:i:s +0900', $lastpost); ?></pubDate>
	<language><?php echo LANGUAGE; ?></language>
<?php foreach ( $posts as $post ) { ?>
	<item>
		<title><?php echo $post['title']; ?></title>
		<link><?php echo $post['permalink']; ?></link>
		<pubDate><?php echo date('D, d M Y H:i:s +0900', $post['postdate']); ?></pubDate>
		<guid><?php echo $post['permalink']; ?></guid>
		<description><![CDATA[<?php echo $post['description']; ?>]]></description>
<?php foreach ((array) $post['tags'] as $tag) { ?>
		<category><![CDATA[<?php echo $tag; ?>]]></category>
<?php } ?>
	</item>
<?php } ?>
</channel>
</rss>

ざっと、こんな所です。

まず、Snoopy (simulates a web browser) を使用して、公式サイトのオンエア状況を取得します。(17-25行目)

次に取得してきた HTML から PHP Simple HTML DOM Parser を使用して、目的の情報を取り出します。(27-51行目)
PHP Simple HTML DOM Parser は、目的の DOM 要素を割りと簡単に取得できるのでお気に入りです。

で、最後に RSS フィードを吐き出す。(53-79行目)

簡単ですね。
これで、最新オンエア曲を吐き出す RSS フィードが出来上がりました。

RSSフィードを確認して、新しい情報があれば Twitter に投稿

RSSフィードさえできてしまえば、twitterfeed.com などの外部サービスを利用して、Twitter に投稿すれば良いのですが、これを自前でやるプログラムも提示しておきます。

<?php
// ライブラリをインクルード
require( 'dacRssParse.php' );

// 定数の設定
define( 'RSS_FEED',    'http://example.com/fm-hoge-on-air.php' );

define( 'TWITTER_USR', 'twitter_user_id' );
define( 'TWITTER_PWD', 'password' );
define( 'TWITTER_URL', 'http://twitter.com/statuses/update.xml?' );

define( 'UPDATE_TIME', 'update_time_hoge.txt' );

// 前回の最終投稿日時をファイルから取得
if (file_exists(UPDATE_TIME)) {
	$fp = fopen(UPDATE_TIME, 'r');
	$lastdate = strtotime(fread($fp, filesize(UPDATE_TIME)));
	fclose($fp);
} else {
	$lastdate = time();
}

// 指定の RSS フィードを順次読み込み Twitter に投稿する
$rss = new CDaRssParse;
$rss->setDebug(TRUE);
$rss->setFetchType($rss->FETCH_FILE);
if( $rss->parse(RSS_FEED) ) {
	$channel = $rss->getChannel();
	$pubdate = strtotime(isset($channel['pubDate']) ? $channel['pubDate'] : '');
	if ($pubdate > $lastdate) {
		foreach (array_reverse($rss->getItems()) as $item) {
			// オンエア時間が前回投稿時間よりも新しかったら Twitter に投稿する
			$pubdate = strtotime(isset($item['pubDate']) ? $item['pubDate'] : '');
			if ($pubdate > $lastdate) {
				// Twitter に投稿する情報を組み立てる
				$title = (isset($item['title']) ? $item['title'] : '');
				$link  = (isset($item['link']) ? ' '.get_tiny_url($item['link']) : '');
				$post  = $title . ' (' . date('Y.n.j G:i', $pubdate) . ')';
				$lastdate = $pubdate;

				// Twitter に投稿
				$params = "status=". rawurlencode($post);
				$result = file_get_contents(
					TWITTER_URL . $params ,
					false ,
					stream_context_create(array(
						"http" => array(
							"method" => "POST" ,
							"header" => "Authorization: Basic ". base64_encode(TWITTER_USR . ":" . TWITTER_PWD)
						)
					)));
			}
		}
		$lastdate = $pubdate;
	}
}
unset($rss);

// 最終投稿日時をファイルに保存
if(time() < $lastdate) $lastdate = time();
$fp = fopen(UPDATE_TIME, 'w');
fwrite($fp, gmdate("M d Y H:i:s", $lastdate).' GMT');
fclose($fp);
?>

今回は RSS の解析に daRssParser v1.0.3 を使用させていただきました。
以下のリンク先で配布されています。
DA実験室 :: PHP+MySQLのスクリプト配布

まず、ファイルに保存しておいた前回の最終投稿日時を取得。(14-21行目)

次に daRssParser で、RSSフィードを取得して、オンエア時間が前回投稿時間よりも新しかったら Twitter に投稿します。(23-57行目)

最後に最終投稿日時をファイルに保存して終了です。(59-63行目)

Now On Air」への7件のフィードバック

  1. tobikagerou twitter

    ラジオで流れてる曲のリストを取得してつぶやいてくれるプログラムを作成してるとこ見つけた。ZIPを良く聞くおいらにとっちゃあ神がかってるぜこりゃあ。 http://j.mp/gTqaeF

  2. hiro55bs twitter

    ありがとうございます!ソースをパク…流用させてもらえれば色々と実装できそうですね。 RT @wokamoto: @hiro55bs php です。解説はうちのブログで http://dogmap.jp/AN 今はOAuthに対応させてあります。 #nds14

  3. ピンバック: Twitter OAuth 対応の話 : dogmap.jp Japan WordPress

  4. ピンバック: M's Life 2 » ZIP-FM Now On Air Japan WordPress

コメントを残す

メールアドレスが公開されることはありません。

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>