趣味の日曜プログラムシリーズ・kintoneを利用して、プロ野球のニュースを集めるアンテナサイトを開発する試み。
前回は、野球のニュースサイトのフィードURLをkintoneに保存して、API経由で取得するまでを行いました。
今回は、野球ニュースサイトから記事のデータを取得して、新着記事だけをkintoneに自動的にPOSTしていく仕組みを作ります。
大まかな流れ
今回の大まかな流れです。
野球ニュースのデータを登録するkintoneアプリを新たに作る
まず最初に、各野球ニュースサイトから取得したニュース記事情報を登録するkintoneアプリを新たに作成します。前回作成した、野球ニュースサイトのフィードURL管理用のアプリとは別になります。
- 前回作成したkintoneアプリを 「フィードURL管理アプリ」
- 今回作成するkintoneアプリを 「野球ニュース記事管理アプリ」
と呼ぶことにします。
ニュース情報の自動登録用のコードを開発
次に、ニュース情報の自動登録用のコードを開発します。図にすると、以下の様になります。
コードのロジックはおおまかに以下のような流れとなります。
- フィードURL管理アプリから、ニュースサイトのフィードURLを取得し、SimplePieに読み込ませる
- 野球ニュース記事管理アプリから、登録済みの記事データを取得する
- 登録済み記事データから、記事のURLを取得して配列に格納する
- SimplePieを利用して、各野球ニュースサイトから、ニュース記事データを連想配列に格納する
- 3で取得した配列と、4で取得した連想配列の記事URLを1件ずつ比較し、新着記事だけの配列データを生成する
- 5の配列をjsonにエンコードして、野球ニュース記事管理アプリに新規登録していく
- 6の実行結果をログファイルに書き込んでいく
野球ニュース記事管理アプリの作成
まず最初に、kintoneのアプリに登録するデータの定義を行います。
各ニュース記事から、以下のデータを登録することにします。各記事の命名リストを作成
- 記事URL
- ニュース記事のURLデータ。atom、RSS2.0の [link] に当たるデータを取得。
- フィールドコート:NewsURL
- 記事タイトル
- ニュース記事のタイトル。atom、RSS2.0の [title] に当たるデータを取得。
- フィールドコート:NewsTitle
- 記事日時
- ニュース記事の生成日時。atomの [published]、RSS2.0の [pubDate] に当たるデータを取得。
- フィールドコート:NewsDate
- ブログURL
- ニュース記事の引用元となるブログのURL。
- フィールドコート:NewsBlogURL
- ブログ名
- ニュース記事の引用元となるブログの名前
- フィールドコート:NewsBlogTitle
- 記事サマリ
- ニュース記事のフィードデータに含まれる概要データ。atomでいう [summary]、RSSでいう [description] データに当たるデータを取得
- フィールドコート:NewsSummary
kintoneの管理画面にログインして、新たにアプリを作成します。
先ほど定義した、野球ニュース記事の各データ項目を設定していきます。
野球ニュース記事のURLを定義
野球ニュース記事のタイトルを定義
フォームをすべて定義したところ
一覧画面の設定
これで、下準備は完了です。
ニュース記事の自動登録用コードの開発
野球ニュース記事管理アプリができあがったら、自動登録用コードを使用して、野球ニュースの記事を登録していきます。
前回の記事で作成したコードをアレンジして、1ファイルにまとめてみました。
今回作成した自動登録用コードは以下のようになりました。
postRecords.php
<?php // 定数とグローバル変数の定義 define ("SUBDOMAIN" , "kintoneのドメイン"); $header = array( "Host: " . SUBDOMAIN . ".cybozu.com:443", "Content-Type: application/json", "X-Cybozu-Authorization: " . base64_encode('username:password'), ); // SimplePieを呼び出し require_once('autoloader.php'); $feed = new SimplePie(); // 関数get_recordsを使ってアンテナ用のフィードリストを取得 $feedData = get_records(フィードURL管理アプリのID番号); // 取得したフィードリスト $feedData から // フィードのURLを取得して配列 $feedListに代入 if($feedData === FALSE) { if(count($http_response_header) > 0) { $error_message = json_decode($response, true); } } else { $result = json_decode($feedData); foreach($result->records as $records) { $feedList[].=$records->URL->value; } } // SimplePieの関数 set_feed_url に配列 $feedList を読みこませる $feed->set_feed_url($feedList); $feed->init(); // 重複チェックのため、kintoneに保存されている記事データを // 関数 get_records を使用して取得 // NewsURLのみ取り出して配列 $exit_url に格納する $exist_data = json_decode(get_records(野球ニュース記事管理アプリのID番号)); foreach($exist_data -> records as $records) { $exist_url[] .= $records->NewsURL->value; } // SimplePie + $feedlist を利用して、各ニュースサイトの // 記事データを取得し、各要素を変数に格納 foreach ($feed->get_items() as $item){ $NewsURL = $item->get_permalink(); $NewsTitle = $item->get_title(); $NewsDate = $item->get_date('Y-m-d\TH:i:s\Z'); $NewsBlogURL = $item->get_base(); $NewsBlog = $item->get_feed()->get_title(); $NewsSummary = $item->get_description(); // 記事データのNewsURLである$NewsURLを // kintoneに保存されている記事のNewsURL $exit_url と比較 // 一致しない$NewsURLがあった場合、新着記事と判定 // 配列 $array に格納していく if (!in_array($NewsURL, $exist_url)) { $array[] = array( "NewsURL" => array( "value" => $NewsURL ), "NewsTitle" => array( "value" => $NewsTitle ), "NewsDate" => array( "value" => $NewsDate ), "NewsBlogURL" => array( "value" => $NewsBlogURL ), "NewsBlog" => array( "value" => $NewsBlog ), "NewsSummary" => array( "value" => $NewsSummary ), ); } } // 新着記事が存在した場合 = 配列$arrayがtrueだったとき // 関数 post_records 用の引数用jsonデータを生成して、新着記事データをkintoneに保存 if(isset($array)) { $json = array( "app" => "20", "records" => $array, ); post_records(json_encode($json)); } // 関数get_recordsを定義 // 引数にkintoneのアプリIDを取得してkintoneからjsonデータをgetする function get_records($argID) { global $header; $get_context = array( "http" => array( "method" => "GET", "header" => implode("\r\n", $header), "content" => json_encode(array("app" => $argID)) , "ignore_errors" => "true", ) ); return file_get_contents( "https://" . SUB_DOMAIN. ".cybozu.com/k/v1/records.json", false, stream_context_create($get_context) ); } // 関数post_recordsの定義 // 引数に指定されたjsonデータを利用してkintoneにデータをPOSTする // 実行後、$http_response_headerと元にログファイルの記述を行う function post_records($argJson) { global $header; $post_context = array( "http" => array( "method" => "POST", "header" => implode("\r\n", $header), "content" => $argJson, "ignore_errors" => "true", ) ); file_get_contents( "https://" . SUBDOMAIN. ".cybozu.com/k/v1/records.json", false, stream_context_create($post_context) ); $logfile = "./post.log"; $logmes = date(c) . ": " . $http_response_header[0] . "\n" ; file_put_contents($logfile, $logmes, FILE_APPEND | LOCK_EX); } ?>
まず最初に、フィードURL管理アプリからニュースサイトのフィードURLを取得し、SimplePieに読み込ませます。
// SimplePieを呼び出し require_once('autoloader.php'); $feed = new SimplePie(); // 関数get_recordsを使ってアンテナ用のフィードリストを取得 $feedData = get_records(フィードURL管理アプリのID番号); // 取得したフィードリスト $feedData から // フィードのURLを取得して配列 $feedListに代入 if($feedData === FALSE) { if(count($http_response_header) > 0) { $error_message = json_decode($response, true); } } else { $result = json_decode($feedData); foreach($result->records as $records) { $feedList[].=$records->URL->value; } } // SimplePieの関数 set_feed_url に配列 $feedList を読みこませる $feed->set_feed_url($feedList); $feed->init();
次に、野球ニュース記事管理アプリから、登録済みの記事データを取得します。
foreach ($feed->get_items() as $item){ $NewsURL = $item->get_permalink(); $NewsTitle = $item->get_title(); $NewsDate = $item->get_date('Y-m-d\TH:i:s\Z'); $NewsBlogURL = $item->get_base(); $NewsBlog = $item->get_feed()->get_title(); $NewsSummary = $item->get_description();
登録済み記事データから、記事のURLを取得して配列に格納します。
SimplePieを利用し、各野球ニュースサイトからニュース記事データを連想配列に格納して、既存の記事データとURLの値で比較を行い、新着記事だけの配列データを生成します。
foreach ($feed->get_items() as $item){ $NewsURL = $item->get_permalink(); $NewsTitle = $item->get_title(); $NewsDate = $item->get_date('Y-m-d\TH:i:s\Z'); $NewsBlogURL = $item->get_base(); $NewsBlog = $item->get_feed()->get_title(); $NewsSummary = $item->get_description(); // 記事データのNewsURLである$NewsURLを // kintoneに保存されている記事のNewsURL $exit_url と比較 // 一致しない$NewsURLがあった場合、新着記事と判定 // 配列 $array に格納していく if (!in_array($NewsURL, $exist_url)) { $array[] = array( "NewsURL" => array( "value" => $NewsURL ), "NewsTitle" => array( "value" => $NewsTitle ), "NewsDate" => array( "value" => $NewsDate ), "NewsBlogURL" => array( "value" => $NewsBlogURL ), "NewsBlog" => array( "value" => $NewsBlog ), "NewsSummary" => array( "value" => $NewsSummary ), ); } }
新着記事データが存在した場合、記事データを格納した配列をjsonにエンコードして、野球ニュース記事管理アプリに新規登録していき、実行結果をログファイルに書き込みます。
if(isset($array)) { $json = array( "app" => "20", "records" => $array, ); post_records(json_encode($json)); }
このコードを1時間ごとcronで実行して、新着記事をチェック。新しい記事が存在していたら、kintoneに登録するようにしました。
cd /path/to/script ; /usr/local/bin/php ./postRecords.php
実行結果
実行結果です。意図通りにデータの収集を行うことができました。
野球ニュース記事管理アプリのデータ一覧
ログファイルのキャプチャ
備忘録
以下、気がついた点諸々
kintone に日付データをPOSTするためにはISO8601形式でないといけない
日付データのPOSTは、ISO8601形式でなければいけないことに気が付かず、しばらくハマっておりました。
phpのdate関数は、オプションでcをつけるとISO8601形式で出力されることになっています。
kintoneの日付データの持ち方は、ISO8601+UTCのtimezone形式で管理しています。一方で、phpのdate関数に、単純にcを指定するだけだと、timezoneがUTCと非互換になるようです。
このため、この辺りの情報から、書式を借用し、
('Y-m-d\TH:i:s\Z')
としました。
(PHP7からはDatetimeクラスを利用するようになるのかな?このへんまだ良く分かっていない)
REST APIを利用する場合は、大元のソフトウェアが吐き出すJSONデータの形式をよく見て、データをどのような型で持っているかを確認してからのほうが、ロスが少ないと(今さら)実感しました。
次回はJavaScript を利用します
次回はJavaScript を利用して、閲覧ページをPHP(SimplePie) ベースからJavaScriptベースに書き換えてみます。