PHPで政府パブリックコメントを検索・表示

(1/1)
1994年(平成6年)10月施行の行政手続法により、中央省庁が、告示などの命令、審査基準、処分基準、行政指導指針などを定める場合、事前に内容を公示し、意見を求めるパブリックコメント(意見公募)が行われるようになった。
以前は各省庁がバラバラに行っていたが、現在、政府パブリックコメントに情報が集約されている。

ところが、現在どんなパブリックコメントが出ており、いつ締め切りになるのかを一元的に調べる仕組みがないようだ(各省庁のメールマガジンや、非公式なツイート・ボットがあることは確認している)。
そこで、政府パブリックコメントから最新情報を逐次取得しデータベースに登録し、ブラウザから検索・表示できるようなPHPプログラムを作ってみることにする。

(2022年11月12日)MYSELFの取得ロジック見直し

サンプル・プログラムの実行例

PHPで政府パブリックコメントを検索・表示

目次

cronへの登録

政府パブリックコメントを定期的に取得しDB更新する目的で、cronWindowsタスクスケジューラに組み込むことをおすすめする。
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 を参照するようにする。
ところが、このRSSは最新十数件しか保持していないようで、それ以前のパブリックコメントがあっても分からない。電子政府の外部連携APIを利用しようと考えたが、利用登録など手続きが煩雑であり、公開プログラムにするには面倒と判断した。

そこで、RSS情報を、1日数回cronで参照し、データベースに登録し、登録した内容を検索・参照できるようなプログラムを作ることにする。
プログラムの保守性を考え、登録と検索・参照は同一プログラムで実行できるようにする。
DBMSは、PHPからインストール不要で使える SQLite を使うことにする。
PHPで政府パブリックコメントを検索・表示

解説:準備

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 クラス

日時を扱うクラス 「日付と時刻」で紹介した日時を扱うクラス pahooDateTime は、ファイル "pahooDataStructure.php" に含まれている。
クラスについては「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: }

ユーザー関数 getPCinfo は、政府パブリックコメントRSSから必要な情報を配列に代入する。
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_ERRMODEPDO::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: }

本プログラムの実行直後、ユーザー関数 initDB を実行し、DBが存在しなかったら自動生成する。

解説: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: }

ユーザー関数 getPCinfo によって取得したパブリックコメント情報をDBに登録する関数が storeDB である。
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_ERRMODEPDO::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: }

ユーザー関数 makeTable は、DB検索と、検索結果の一覧表作成を同時に行う。

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_ERRMODEPDO::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: }

冒頭で initDB を実行し、必要に応じてDB生成を行う。
次に、isButton 関数を使って、コマンドラインに STORE_DB があれば、関数 storeDB を実行して即終了する。

それ以外の場合は、DB検索を行い、結果をブラウザに表示する。

参考サイト

(この項おわり)
header