趣味の日曜プログラムシリーズ・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ベースに書き換えてみます。