kintoneを使って野球アンテナをつくる その4 kintoneへPOST編

趣味の日曜プログラムシリーズ・kintoneを利用して、プロ野球のニュースを集めるアンテナサイトを開発する試み。

前回は、野球のニュースサイトのフィードURLをkintoneに保存して、API経由で取得するまでを行いました。

今回は、野球ニュースサイトから記事のデータを取得して、新着記事だけをkintoneに自動的にPOSTしていく仕組みを作ります。

大まかな流れ

今回の大まかな流れです。

野球ニュースのデータを登録するkintoneアプリを新たに作る

まず最初に、各野球ニュースサイトから取得したニュース記事情報を登録するkintoneアプリを新たに作成します。前回作成した、野球ニュースサイトのフィードURL管理用のアプリとは別になります。

  • 前回作成したkintoneアプリを 「フィードURL管理アプリ」
  • 今回作成するkintoneアプリを 「野球ニュース記事管理アプリ」

と呼ぶことにします。

ニュース情報の自動登録用のコードを開発

次に、ニュース情報の自動登録用のコードを開発します。図にすると、以下の様になります。

summary.png

コードのロジックはおおまかに以下のような流れとなります。

  1. フィードURL管理アプリから、ニュースサイトのフィードURLを取得し、SimplePieに読み込ませる
  2. 野球ニュース記事管理アプリから、登録済みの記事データを取得する
  3. 登録済み記事データから、記事のURLを取得して配列に格納する
  4. SimplePieを利用して、各野球ニュースサイトから、ニュース記事データを連想配列に格納する
  5. 3で取得した配列と、4で取得した連想配列の記事URLを1件ずつ比較し、新着記事だけの配列データを生成する
  6. 5の配列をjsonにエンコードして、野球ニュース記事管理アプリに新規登録していく
  7. 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の管理画面にログインして、新たにアプリを作成します。

newapli.png

先ほど定義した、野球ニュース記事の各データ項目を設定していきます。

野球ニュース記事のURLを定義

NewsURL.png

野球ニュース記事のタイトルを定義

newstitle.png

フォームをすべて定義したところ

formscreen.png

一覧画面の設定

NewsList.png

これで、下準備は完了です。

ニュース記事の自動登録用コードの開発

野球ニュース記事管理アプリができあがったら、自動登録用コードを使用して、野球ニュースの記事を登録していきます。
前回の記事で作成したコードをアレンジして、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

実行結果

実行結果です。意図通りにデータの収集を行うことができました。

野球ニュース記事管理アプリのデータ一覧

collected_news_data.png

ログファイルのキャプチャ

log.png

備忘録

以下、気がついた点諸々

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ベースに書き換えてみます。