PHPで今日は何の日か調べる(Windowsアプリ版)

(1/1)
PHPを使うと、簡単にサイトの情報を取り出す(スクレイピング)ことができる。「PHPで今日は何の日か調べる」では、Wikipedia および 一般社団法人 日本記念日協会にアクセスし、今日の出来事を調べるプログラムをつくった。今回は、これをWindowsアプリに移植する。

(2021年1月31日)記念日も表示するようにした。

サンプル・プログラム

PHPでWindowsアプリ開発:今日は何の日

目次

解説:calendar

 317: /**
 318:  * 検索結果一覧
 319:  * @param   int $window ウィンドウID
 320:  * @return  string なし
 321: */
 322: function putItems($window) {
 323:     global $Items;
 324: 
 325:     clear_error($window);
 326:     wb_delete_items(wb_get_control($window, IDC_RES_LISTVIEW));
 327:     $Items = array();
 328: 
 329:     $t = wb_get_value(wb_get_control($window, IDC_CALENDAR));
 330:     $month = date('m', $t+ 0;
 331:     $day   = date('d', $t+ 0;
 332: 
 333:     $i = getDayToday($month, $day, $Items);
 334:     $i = getAnniversary($month, $day, $Items, $errmsg, $i);
 335:     usort($Items, 'sortItems');
 336: 
 337:     //表示
 338:     if ($i > 0) {
 339:         wb_create_items(wb_get_control($window, IDC_RES_LISTVIEW), $Items);
 340:     } else {
 341:         put_error($window, ERROR_NOTCONNECTED);
 342:     }
 343: }

月日の選択は、Winowsの calendarコントロール を利用する。
WinBinder関数 wb_get_value を呼び出すことで、現在選択されている年月日をUNIX TIMEで取り出すことができる。これを組み込み関数  date  に通せば、月と日を取得できるという仕組みである。

解説:https通信対応

 134: /**
 135:  * httpsサイトからコンテンツを受信
 136:  * @param   string $url "https://" から始まるURL
 137:  * @param   array  $post POST変数を格納した連想配列("変数名"=>"値") 
 138:  * @return  string 取得したコンテンツ/FALSE 取得エラー
 139: */
 140: function file_get_contents_ssl($url, $post=NULL) {
 141:     $ch = curl_init($url);
 142:     curl_setopt($ch, CURLOPT_HEADER, FALSE);
 143:     curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
 144:     if ($post !NULL) {
 145:         curl_setopt($ch, CURLOPT_POST, TRUE);
 146:         curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
 147:     }
 148:     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);    //サーバ証明書検証をスキップ
 149:     curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);    //  〃
 150:     $result = curl_exec($ch);
 151:     curl_close($ch);
 152: 
 153:     return $result;
 154: }

Wikipediaのサイトが https 化したことにともない、コンテンツの取得をhttpsプロトコルで行わなければならない。 fopen  や  file_get_contents  では bamcompile がhttpsプロトコルに対応していないようなので、「PHPセキュリティ対策:SSL通信を行う」で紹介したCURL, Client URL Library 関数を利用することにする。
php.iniextension=php_curl を有効にする必要があるため、本プログラム線用の php.ini を "daytodaywin\" ディレクトリに配置し、コンパイル時に読み込むようにしてある。

第二引数 $post を指定したときは、POST渡しできるようにしている。これは、日本記念日協会にアクセスするときに使う。

解説:Wikipediaをスクレイピング

 190: /**
 191:  * 今日は何の日 取得
 192:  * @param   int $month, $day 月,日
 193:  * @param   array $items 情報格納配列
 194:  * @param   bool $lfn 脚注残す(省略時=FALSE)
 195:  * @return  int 情報件数
 196: */
 197: function getDayToday($month, $day, &$items, $lfn=FALSE) {
 198:     $pat1 = "<span[\s\S]+id=\"できごと\">";
 199:     $pat2 = "<span[\s\S]+id=\"誕生日\">";
 200:     $pat3 = "<span[\s\S]+id=\"忌日\">";
 201:     $pat5 = "<li>[^\>]+([0-9]+年.+)<\/li>";
 202:     $pat6 = "([^0-9]*)([0-9]+)年(\s*[\((].[^\))]+[)\)].)?[ \-]+(.+)";    //年 - イベント
 203:     $pat7 = "([^0-9]*)([0-9]+)年[ \-]+(.+)?、([^(\(]+)*[^0-9]*([0-9]*)";
 204:                                                     //年 - 名前、職業(生没年)
 205:     $pat8 = "<span[\s\S]+id=\"フィクションのできごと\">";
 206: 
 207:     //コンテンツ読み込み
 208:     $url = getURL_Wikipedia($month, $day);
 209:     $contents = file_get_contents_ssl($url);
 210:     if ($contents == FALSE)     return FALSE;
 211: 
 212:     //解釈
 213:     $str = strtok($contents, "\n");
 214:     $mode = '';
 215:     $cnt = 0;
 216:     while ($str !FALSE) {
 217:         if (mb_eregi($pat1, $str) == 1) {
 218:             $mode = '出来事';
 219:         } else if (mb_eregi($pat8, $str) == 1) {
 220:             $mode = '架空';
 221:         } else if (mb_eregi($pat2, $str) == 1) {
 222:             $mode = '誕生';
 223:         } else if (mb_eregi($pat3, $str) == 1) {
 224:             $mode = '死亡';
 225:         } else if ($mode !'' && mb_eregi($pat5, $str, $arr> 0) {
 226:             $str = strip_tags(trim($str));
 227:             if ($mode == '出来事' && mb_eregi($pat6, $str, $arr> 1) {
 228:                 $items[$cnt][0] = internal2sjis($mode);
 229:                 $items[$cnt][1] = ($arr[1] == ''? (int)$arr[2: 0 - $arr[2];
 230:                 $items[$cnt][2] = internal2sjis($lfn ? $arr[4: eliminate_footnote($arr[4]));
 231:                 $cnt++;
 232:             } else if (mb_eregi($pat7, $str, $arr> 1) {
 233:                 $items[$cnt][0] = internal2sjis($mode);
 234:                 $items[$cnt][1] = ($arr[1] == ''? (int)$arr[2: 0 - $arr[2];
 235:                 $items[$cnt][2] = internal2sjis($lfn ? $arr[3: eliminate_footnote($arr[3]) . '(' . $arr[4]);
 236:                 if ($arr[5!'') {
 237:                     if ($mode == '誕生') {
 238:                         $items[$cnt][2.internal2sjis(';~' . $arr[5. '年');
 239:                     } else {
 240:                         $items[$cnt][2.internal2sjis(';' . $arr[5. '年~');
 241:                     }
 242:                 }
 243:                 $items[$cnt][2.internal2sjis(')');
 244:                 $cnt++;
 245:             }
 246:         }
 247:         $str = strtok("\n");
 248:     }
 249: 
 250:     return $cnt;
 251: }

スクレイピングは正規表現を使って実装する。正規表現パターンは「PHPで今日は何の日か調べる」で説明したとおりである。
PHPでテキストの読みやすさを調べる(Windowsアプリ版)」で紹介したとおり、preg 系関数に動作不具合があるようなので、すべて mb_erege 系関数で処理している。

検索結果は「PHPで Windows用国立国会図書館検索プログラムをつくる」で使った ListView である。

解説:日本記念日協会をスクレイピング

 253: /**
 254:  * 日本記念日協会のページをスクレイピングして記念日を取り出す
 255:  * @param   string $contents ページ内容
 256:  * @param   array  $items 記念日を格納する配列
 257:  * @param   int $cnt 情報を追加する配列の最初の番号
 258:  * @return  int 情報の総数
 259: */
 260: function scrapingAnniversary($contents, &$items, $cnt) {
 261:     $pat = '<a\s+class="winDetail"\s+href="([^"]+)"><font[^>]+>([^<]+)</font>';
 262:     $mode = '記念日';
 263: 
 264:     $tok = strtok($contents, "\n");
 265:     while ($tok !FALSE) {
 266:         if (mb_eregi($pat, $tok, $arr> 0) {
 267:             $items[$cnt][0] = internal2sjis($mode);
 268:             $items[$cnt][1] = '';
 269:             $items[$cnt][2] = internal2sjis($arr[2]);
 270:             $cnt++;
 271:         }
 272:         $tok = strtok("\n");
 273:     }
 274: 
 275:     return $cnt;
 276: }

 278: /**
 279:  * 指定月日の記念日を取得する
 280:  * @param   int $month, $day 月日
 281:  * @param   array $items   記念日を格納する配列
 282:  * @param   string $errmsg エラーメッセージ格納用
 283:  * @param   int $cnt 情報を追加する配列の最初の番号
 284:  * @return  int 情報の総数
 285: */
 286: function getAnniversary($month, $day, &$items, &$errmsg, $cnt) {
 287:     //パラメータをPOST渡しする
 288:     $url = KINENBI_URL;
 289:     $post = array(
 290:         'MD' => $month,
 291:         'M'  => $month,
 292:         'D'  => $day
 293:     );
 294:     $errmsg = '';
 295: 
 296:     $res = file_get_contents_ssl($url, $post);
 297:     if ($res == FALSE) {
 298:         $errmsg = '日本記念日協会のサイトにアクセスできません.';
 299:     } else {
 300:         $cnt = scrapingAnniversary($res, $items, $cnt);
 301:     }
 302: 
 303:     return $cnt;
 304: }

これは、「PHPで記念日を表示する」で紹介した [../php05/php05-21-01.shtm#scrapingAnniversary] および [../php05/php05-21-01.shtm#getAnniversary] 関数をPHP4に移植したものである。
日本記念日協会から、指定した月日の記念日を取得する。

コンパイル

コマンドラインから "bamcompile daytodaywin.bcp" を実行する。コンパイルが完了すると、"daytodaywin.exe" が生成される。"daytodaywin.exe" は、DLL不要で、単独で動作するEXEプログラムである。

参考サイト

(この項おわり)
header