サンプル・プログラムの実行例
目次
cronへの登録
DB登録のためのコマンドラインオプションは、後述する定数 STORE_DB に定義したものを使う。たとえば定義値が "store123" であれば、cronに下記のように記述する。
/usr/bin/php /e-soul/webtech/php05/program/getPublicComment.php store1234567890=0
サンプル・プログラムのダウンロード
getPublicComment.php | サンプル・プログラム本体。 |
pahooDataStructure.php | 日時を使うためのクラス pahooDateTime。 使い方は「日付と時刻」を参照のこと。include_pathが通ったディレクトリに配置する。 |
プログラムの方針
ところが、このRSSは最新十数件しか保持していないようで、それ以前のパブリックコメントがあっても分からない。電子政府の外部連携APIを利用しようと考えたが、利用登録など手続きが煩雑であり、公開プログラムにするには面倒と判断した。
そこで、RSS情報を、1日数回cronで参照し、データベースに登録し、登録した内容を検索・参照できるようなプログラムを作ることにする。
プログラムの保守性を考え、登録と検索・参照は同一プログラムで実行できるようにする。
DBMSは、PHPからインストール不要で使える SQLite を使うことにする。
解説:準備
0046: //表示幅(ピクセル)
0047: define('WIDTH', 600);
0048:
0049: //タイムゾーン(日本標準時)
0050: define('TZONE', '+09:00');
0051:
0052: //電子政府パブリックコメントRSS
0053: define('PC_RSS_URL', 'https://public-comment.e-gov.go.jp/rss/pcm_list.xml');
0054:
0055: //SQLite DBファイル名:各自の環境に合わせて変更すること
0056: define('DBFILE', './publicComment.sqlite3');
0057:
0058: //SQLite テーブル名
0059: define('TABLE_PC', 'public_comment'); //パブリックコメント情報
0060:
0061: //DB登録用キー
0062: define('STORE_DB', 'store1234567890');
0063:
0064: //実行するSQL
0065: define('PRE_SELECT', 'SELECT * FROM ' . TABLE_PC . ' WHERE id=:id');
0066: define('PRE_INSERT', 'INSERT INTO ' . TABLE_PC . ' (id, title, uri, announce_dt, close_dt, category, refer, premiere, latest) VALUES (:id, :title, :uri, :announce_dt, :close_dt, :category, :refer, :premiere, :latest)');
0067: define('PRE_UPDATE', 'UPDATE ' . TABLE_PC . ' set title=:title, uri=:uri, announce_dt=:announce_dt, close_dt=:close_dt, category=:category, refer:=refer, latest=:latest WHERE id=:id');
0068: define('PRE_QUERY', 'SELECT * FROM ' . TABLE_PC . ' WHERE close_dt >= :close_dt ORDER BY close_dt DESC');
0069: define('PRE_LATEST', 'SELECT latest FROM ' . TABLE_PC . ' WHERE 1 ORDER BY latest DESC');
0070:
0071: //データ構造に関わるクラス:include_pathが通ったディレクトリに配置
0072: require_once('pahooDataStructure.php');
SQLite のデータベース実体ファイルは定数 DBFILE に定義する。PHPから書き込み可能なファイルであれば、フォルダやファイル名は自由に設定してかまわない。
本プログラム中のユーザー関数 initDB によってデータベースを自動生成するので、事前準備は不用である。
前述の通り、cronでDB登録する際、コマンドラインに渡すキーを STORE_DB に定義する。実害はないが、外部から手動でDB登録されるのも気持ちが悪いので、複雑なキーにしておくといいだろう。なお、ダウンロードしたキーでは、本サイトでDB登録を行うことはできない。
解説:pahooDataStructure クラス
クラスについては「PHPでクラスを使ってテキストの読みやすさを調べる」で解説している。
クラスファイル "pahooDataStructure.php" は、組み込み関数 require_once を使って読めるディレクトリに配置する。ディレクトリは、設定ファイル php.ini に記述されているオプション設定 include_path に設定しておく。
解説:パブリックコメント情報の取得
0266: /**
0267: * 電子政府 パブリックコメントRSSをスクレイピングし
0268: * パブリックコメント情報を配列に格納
0269: * @param string $url スクレイピングするURL
0270: * @param array $items パブリックコメント情報を格納する配列
0271: * @return int 取得情報件数/FALSE:失敗
0272: */
0273: function getPCinfo($url, &$items) {
0274: define('NS_dc', 'http://purl.org/dc/elements/1.1/'); //名前空間
0275: static $pat1 = "/公示日:([0-9\/]+)/iu";
0276: static $pat2 = "/締切日時:([0-9\/]+)/iu";
0277: static $pat3 = "/問合せ先[^:]+:([^\s]+)/iu";
0278: static $pat4 = "/<br\s*\/>/iu";
0279: static $pat5 = "/\&id\=([0-9]+)/iu";
0280: static $pat6 = "/カテゴリー:(.+)/iu";
0281:
0282: $pdt = new pahooDateTime(); //日時計算クラス
0283:
0284: $rss = simplexml_load_file($url);
0285: if ($rss == FALSE) return FALSE;
0286:
0287: $cnt = 0;
0288: foreach ($rss->item as $item) {
0289: $items[$cnt]['id'] = (preg_match($pat5, $item->link, $arr) > 0) ?
0290: (int)$arr[1] : '';
0291: $items[$cnt]['title'] = trim((string)$item->title);
0292: $items[$cnt]['uri'] = trim((string)$item->link);
0293: $str = trim((string)$item->description);
0294: $items[$cnt]['announce_dt'] = (preg_match($pat1, $str, $arr) > 0) ?
0295: yyyymmdd2iso8601($arr[1]) : '';
0296: $items[$cnt]['close_dt'] = (preg_match($pat2, $str, $arr) > 0) ?
0297: yyyymmdd2iso8601($arr[1]) : '';
0298: $items[$cnt]['refer'] = (preg_match($pat3, $str, $arr) > 0) ?
0299: strip_tags($arr[1]) : '';
0300: $arr = preg_split($pat4, $str);
0301: $str = $arr[2];
0302: $items[$cnt]['category'] = (preg_match($pat6, $str, $arr) > 0) ?
0303: strip_tags($arr[1]) : '';
0304: //更新日時
0305: $node = $item->children(NS_dc);
0306: $pdt->parse_date((string)$node->date);
0307: $dt = $pdt->date('c');
0308: $items[$cnt]['latest'] = $dt;
0309: $cnt++;
0310: }
0311:
0312: $pdt = NULL;
0313: return ($cnt - 1);
0314: }
RSSのパーシングは、「PHPでコロンを含むXML要素名を扱う方法」で紹介したように、SimpleXML を使う。
公示日、締切日、所轄官庁、連絡先窓口といった情報は全て descriptionタグに含まれているため、その内容を preg_match 関数を使ってスクレイピングし、配列へと格納してゆく。
また、日時情報は世界時をベースにした ISO 8601 でデータベースに格納すべく、先ほど紹介したクラス pahooDateTime を使う。
解説:DBの初期化
0338: /**
0339: * DBの初期化
0340: * @param なし
0341: * @return bool TRUE/FALSE
0342: */
0343: function initDB() {
0344: try {
0345: $pdo = new PDO('sqlite:' . DBFILE);
0346: $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
0347:
0348: //テーブル作成:パブリックコメント
0349: //id:案件番号, title:題名, uri:リンク先
0350: //announce_dt:公示日, close_dt:締切日
0351: //category:カテゴリ, refer:問い合わせ先
0352: //premiere:登録日時, latest:更新日時
0353: $pdo->exec('CREATE TABLE IF NOT EXISTS ' . TABLE_PC . '(
0354: id INTEGER PRIMARY KEY AUTOINCREMENT,
0355: title TEXT,
0356: uri TEXT,
0357: announce_dt TEXT,
0358: close_dt TEXT,
0359: category TEXT,
0360: refer TEXT,
0361: premiere TEXT,
0362: latest TEXT
0363: )');
0364: $res = TRUE;
0365: } catch (PDOException $e) {
0366: $res = FALSE;
0367: }
0368:
0369: return $res;
0370: }
解説:DBにパブリックコメントを登録する
0372: /**
0373: * 電子政府 パブリックコメントRSSをDB登録する
0374: * @param string $url 電子政府 パブリックコメントRSS
0375: * @return int 登録件数
0376: */
0377: function storeDB($url) {
0378: $item = array();
0379: $cnt = 0;
0380: getPCinfo($url, $items);
0381: foreach ($items as $item) {
0382: $res = updatePCtoDB($item['id'], $item);
0383: if ($res == TRUE) $cnt++;
0384: }
0385: return $cnt;
0386: }
1件1件のパブリックコメントに対し、updatePCtoDB 関数を実行する。
ユーザー関数 updatePCtoDB は、DBレコードを更新する。
案件番号をキーにして、未登録のパブリックコメントであれば insertPCtoDB 関数を使ってDBにinsertする。案件番号が存在しており、更新日が異なっていればDBをupdateする。
ユーザー関数 insertPCtoDB は、パブリックコメント情報をDBにinsertする。
ユーザー関数 getPCfromDB は、案件番号をキーにしてDBをselectする。
解説:DB検索および一覧表HTMLを作成する
0503: /**
0504: * DB検索および一覧表HTMLを作成する
0505: * @param string $query 検索キー
0506: * @return string 一覧表HTML
0507: */
0508: function makeTable($query) {
0509: $pdt = new pahooDateTime(); //日時計算クラス
0510: $pat = '/' . $query . '/ui';
0511: $update = '';
0512:
0513: $html =<<< EOT
0514: <table class="pc">
0515: <tr>
0516: <th>案件番号</th>
0517: <th>題名</th>
0518: <th>公示日</th>
0519: <th>締切日</th>
0520: </tr>
0521:
0522: EOT;
0523: try {
0524: $pdo = new PDO('sqlite:' . DBFILE);
0525: $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
0526: $stmt = $pdo->prepare(PRE_QUERY);
0527: //締切日は本日より未来日
0528: $stmt->bindValue(':close_dt', date('Y-m-d', time()), PDO::PARAM_STR);
0529: $res = $stmt->execute();
0530: while ($row = $stmt->fetch()) {
0531: //検索キーとのマッチング
0532: if ((preg_match($pat, $row['title']) == 0) &&
0533: (preg_match($pat, $row['refer']) == 0) &&
0534: (preg_match($pat, $row['category']) == 0)) continue;
0535: //データ取得
0536: $pdt->parse_date($row['announce_dt']);
0537: $pdt->tzone(TZONE);
0538: $announce_dt = $pdt->date('Y/m/d');
0539: $pdt->parse_date($row['close_dt']);
0540: $pdt->tzone(TZONE);
0541: $close_dt = $pdt->date('Y/m/d');
0542: $html .=<<< EOT
0543: <tr>
0544: <td class="id"><a href="{$row['uri']}" target="_blank">{$row['id']}</a></td>
0545: <td class="title">{$row['title']}</td>
0546: <td class="dt">{$announce_dt}</td>
0547: <td class="dt">{$close_dt}</td>
0548: </tr>
0549: <tr>
0550: <td colspan="2" class="description">{$row['refer']}</td>
0551: <td colspan="2" class="description">{$row['category']}</td>
0552: </tr>
0553:
0554: EOT;
0555: }
0556: //DB最終登録更新日の取得(RSSより)
0557: $stmt = $pdo->prepare(PRE_LATEST);
0558: $res = $stmt->execute();
0559: $row = $stmt->fetch();
0560: if ($row != FALSE) {
0561: $dt = $row['latest'];
0562: $pdt->parse_date($dt);
0563: $pdt->tzone(TZONE);
0564: $update = $pdt->date('Y/m/d H:i');
0565: }
0566: } catch (PDOException $e) {
0567: $pdo = NULL;
0568: return FALSE;
0569: }
0570: $pdo = NULL;
0571:
0572: $html .=<<< EOT
0573: <tr><td colspan="4" class="update">最終更新日時:{$update}</td></tr>
0574: </table>
0575:
0576: EOT;
0577: $pdt = NULL;
0578: return $html;
0579: }
DB検索については、締切日が本日より未来日になるようにSQLで絞り込む。
検索キーは、題名、カテゴリ、問い合わせ先を対象に検索することから、SQLではなく、PHPプログラムの方で preg_match 関数を使ってマッチングを行う。
解説:メイン・プログラム
0338: /**
0339: * DBの初期化
0340: * @param なし
0341: * @return bool TRUE/FALSE
0342: */
0343: function initDB() {
0344: try {
0345: $pdo = new PDO('sqlite:' . DBFILE);
0346: $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
0347:
0348: //テーブル作成:パブリックコメント
0349: //id:案件番号, title:題名, uri:リンク先
0350: //announce_dt:公示日, close_dt:締切日
0351: //category:カテゴリ, refer:問い合わせ先
0352: //premiere:登録日時, latest:更新日時
0353: $pdo->exec('CREATE TABLE IF NOT EXISTS ' . TABLE_PC . '(
0354: id INTEGER PRIMARY KEY AUTOINCREMENT,
0355: title TEXT,
0356: uri TEXT,
0357: announce_dt TEXT,
0358: close_dt TEXT,
0359: category TEXT,
0360: refer TEXT,
0361: premiere TEXT,
0362: latest TEXT
0363: )');
0364: $res = TRUE;
0365: } catch (PDOException $e) {
0366: $res = FALSE;
0367: }
0368:
0369: return $res;
0370: }
参考サイト
- 政府パブリックコメント
- 日付と時刻:ぱふぅ家のホームページ
以前は各省庁がバラバラに行っていたが、現在、政府パブリックコメントに情報が集約されている。
ところが、現在どんなパブリックコメントが出ており、いつ締め切りになるのかを一元的に調べる仕組みがないようだ(各省庁のメールマガジンや、非公式なツイート・ボットがあることは確認している)。
そこで、政府パブリックコメントから最新情報を逐次取得しデータベースに登録し、ブラウザから検索・表示できるようなPHPプログラムを作ってみることにする。
(2022年11月12日)MYSELFの取得ロジック見直し