nasneにキーワード録画機能が無いので、テレビ王国から番組表をクローラして、メール通知してくれるWebシステムをRaspberry Piで作ってみた。
php + MySQL のいわゆるLAMP環境。自宅のRaspberry Piサーバに実装してみた。
1,まず番組情報を入れるためのテーブルをMySQLに作っておく。
1 2 3 4 5 6 7 8 9 10 11 12 |
CREATE TABLE tbl_tv_program( id int(11) NOT NULL AUTO_INCREMENT, start_datetime datetime NULL, end_datetime datetime NULL, duration int NULL, channel varchar(255) NULL, title varchar(255) NULL, detail varchar(255) NULL, url varchar(255) NULL, create_date timestamp default current_timestamp, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
2,毎朝4時くらいに番組情報を取得するphpを, cronで実行させる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
<?php require_once './dsn.php'; //タイムゾーン設定 date_default_timezone_set('Asia/Tokyo'); //MySQLに接続 try { $pdo = new PDO('mysql:dbname='. $dsn['dbname'] .';host=' . $dsn['host'], $dsn['user'], $dsn['pass']); } catch (PDOException $e) { exit('データベースに接続できませんでした。' . $e->getMessage()); } $stmt = $pdo->query('SET NAMES utf8'); if (!$stmt) { $info = $pdo->errorInfo(); exit($info[2]); } //地デジ、BSのみ $broadcast_kind = array('23', 'bs1'); for($broadcast_cnt=0; $broadcast_cnt < count($broadcast_kind); $broadcast_cnt++){ //最初だけ一週間分の日付を生成、以降は一週間後の一日分だけを取得 //$week_start = 0; $week_start = 6; for($day_num = $week_start; $day_num<7; $day_num++){ $mod_day = sprintf("+%d day", $day_num); $week_date = sprintf("%d", date( "Ymd" , strtotime( $mod_day ))); //地デジ $week_url[$day_num] = sprintf("http://tv.so-net.ne.jp/chart/%s.action?head=%s0500&span=24", $broadcast_kind[$broadcast_cnt], $week_date); //その日のテレビ欄を取得 $day_prog = file_get_contents($week_url[$day_num]); $prog_num = preg_match_all('/schedule\/[0-9]+\.action/', $day_prog, $today_prog_res); //1日の番組数だけループ(おおよそ350~400番組) for($i=0; $i < $prog_num; $i++){ // 各番組のURL・内容を取得 $url = "http://tv.so-net.ne.jp/".$today_prog_res[0][$i]; $prog_content = file_get_contents($url); // 放送日時 $num = preg_match_all('/[0-9]+\/[0-9]+ \([a-zA-Z]+\) [0-9]+:[0-9]+ ~ [0-9]+:[0-9]+&nbsp;([0-9]+分)/', $prog_content, $res); $str_datetime = str_replace("&nbsp;","", $res[0][0]); // 形式: 3/15 (Sat) 1:05 ~ 1:35(30分) // 放送日付 $num = preg_match_all('/[0-9]+\/[0-9]+/', $str_datetime, $res); $prog_date = $res[0][0]; // 放送開始時刻 $num = preg_match_all('/[0-9]+:[0-9]+ ~/', $str_datetime, $res); $prog_start_time = str_replace(" ~","", $res[0][0]); $start_datetime = substr($week_date, 0, 4) ."/". $prog_date ." ". $prog_start_time . ":00"; // 放送終了時刻 $num = preg_match_all('/~ [0-9]+:[0-9]+/', $str_datetime, $res); $prog_end_time =str_replace("~ ","", $res[0][0]); $end_datetime = substr($week_date, 0, 4) ."/". $prog_date ." ". $prog_end_time . ":00"; // 放送の長さ(分) $num = preg_match_all('/([0-9]+分)/', $str_datetime, $res); $arr_del = array('(', '分)'); $duration = str_replace($arr_del, "", $res[0][0]); // チャンネル $num = preg_match_all('/\<dd\>.+Ch.+\<\/dd\>/', $prog_content, $res); $arr_del = array('<dd>', '</dd>'); $channel = str_replace($arr_del, "", $res[0][0]); // 番組タイトル $num = preg_match_all('/\<dd\>.+\<a href=\"\/webSearch\.action/', $prog_content, $res); $arr_del = array('<a href="/webSearch.action', '<dd>'); $title = str_replace($arr_del, '', $res[0][0]); // 番組詳細 $num = preg_match_all('/\>番組詳細\<.+\<div class=\"basicTxt\"\>/s', $prog_content, $res); //改行とかタブとか入っているので注意!! $arr_del = array( '>番組詳細</h3>'.PHP_EOL, ' <p class="basicTxt">'.PHP_EOL, ' ', '<br /><br />'.PHP_EOL, ' </p>'.PHP_EOL, ' <div class="basicTxt">', ' &nbsp;' ); $detail = str_replace($arr_del, '', $res[0][0]); //番組内容がない場合は、空文字を入れる。 $str = "basicTxt"; if(strpos($detail, $str) === false){ }else{ $detail = ""; } //番組情報を表示 printf("<b>NO: </b>%d<br>\n", $i); printf("<b>URL: </b>%s<br>\n", $url); printf("<b>TIME: </b>%s<br>\n", $start_datetime); printf("<b>Channel: </b>%s<br>\n", $channel); printf("<b>TITLE: </b>%s<br>\n", $title); printf("<b>DETAIL: </b>%s<br><br>\n", $detail); //取得した情報をMySQLに格納 //セキュリティを考えて、SQL文はプリペアドステートメントで発行。 $stmt = $pdo -> prepare("INSERT INTO tbl_tv_program (start_datetime, end_datetime, duration, channel, title, detail, url) VALUES (:start_datetime, :end_datetime, :duration, :channel, :title, :detail, :url)"); $stmt->bindParam(':start_datetime', $start_datetime, PDO::PARAM_STR); $stmt->bindParam(':end_datetime', $end_datetime, PDO::PARAM_STR); $stmt->bindParam(':duration', $duration, PDO::PARAM_INT); $stmt->bindParam(':channel', $channel, PDO::PARAM_STR); $stmt->bindParam(':title', $title, PDO::PARAM_STR); $stmt->bindParam(':detail', $detail, PDO::PARAM_STR); $stmt->bindParam(':url', $url, PDO::PARAM_STR); $stmt->execute(); if (!$stmt) { $info = $pdo->errorInfo(); exit($info[2]); } //サーバを一秒休ませる。 sleep(1); } } } // MySQLから切断 $pdo = null; |
3, 番組DBが更新されたら、決められたキーワードで番組を検索してメールを送るphpを実行する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
<?php // キーワードを適当に決めておく $keyword = array("秋葉原","アキバ","クッキング","激セマ","食べ放題","寿司","虎ノ門","神話","新橋", "夜話"); require_once './dsn.php'; //キーワードで番組検索 try { $pdo = new PDO('mysql:dbname='. $dsn['dbname'] .';host=' . $dsn['host'], $dsn['user'], $dsn['pass']); } catch (PDOException $e) { exit('データベースに接続できませんでした。' . $e->getMessage()); } $stmt = $pdo->query('SET NAMES utf8'); if (!$stmt) { $info = $pdo->errorInfo(); exit($info[2]); } $mail_text = ""; // キーワードの数だけループ for($word_cnt=0; $word_cnt < count($keyword); $word_cnt++){ //セキュリティを考えて、SQL文はプリペアドステートメントで発行。 $stmt = $pdo->prepare("SELECT id, DATE_FORMAT(start_datetime,'%c月%d日 %k:%i') as start_datetime, end_datetime, duration, channel, title, detail, url, create_date FROM tbl_tv_program WHERE detail like ? limit 10"); $stmt->execute(array('%'.$keyword[$word_cnt].'%')); if (!$stmt) { $info = $pdo->errorInfo(); exit($info[2]); } $i = 0; $section_text = ""; // キーワード毎にセクションに分ける $section_text .= sprintf("<b>%s</b><br>", $keyword[$word_cnt]); while ($data = $stmt->fetch(PDO::FETCH_ASSOC)) { $section_text .= sprintf("%s(%d分)<br><a href='%s'>%s</a><br><br>\n\n", $data['start_datetime'], $data['duration'], $data['url'], $data['title']); $i++; } if($section_text == sprintf("<b>%s</b><br>", $keyword[$word_cnt])){ $mail_text .= sprintf("<b>%s</b><br>無し<br><br>", $keyword[$word_cnt]); }else{ $mail_text .= $section_text; } } // キーワードで番組検索した結果をメールする。 header('Content-Type: text/html; charset=UTF-8'); header('Content_Language: ja'); $to = 'tekito@gmail.com'; //送信先メールアドレス $subj = "本日の検索結果"; //件名 $mess = $mail_text; //本文 $from = 'Content-type: text/html; charset=UTF-8' . "\r\n"; $from .= "From:" .mb_encode_mimeheader("キーワード検索システム") ."<tekito@gmail.com>"; //送信元メールアドレス mb_language('uni'); mb_internal_encoding('UTF-8'); mb_send_mail($to, $subj, $mess, $from); //メール送信 print($mail_text); $pdo = null; |
こんな感じのメールが届く
秋葉原
3月22日 5:00(55分)
熱中世代 大人のランキング 演歌の女王八代亜紀が本音を語る
3月23日 2:00(30分)
でんぱの神神 #47
3月24日 18:30(30分)
アタックしますけど何か?
3月24日 19:00(30分)
アタックしますけど何か?
アキバ
3月22日 17:30(25分)
アニメ ログ・ホライズン「天秤祭(てんびんさい)」[字][デ]
クッキング
3月19日 18:55(30分)
Rの法則「女子高校生の食べたい!餃子」
3月20日 0:00(30分)
Rの法則「女子高校生の食べたい!餃子」[再]
3月20日 8:30(85分)
はなまるマーケット「落合シェフ看板パスタ生直伝▽三宅裕司▽クイズママダス」[字][デ]
3月22日 1:40(30分)
妄想ニホン料理「幸せ包んで 大福の巻」[字][再]
一応ちゃんと使えるけど、一週間近く先の番組がヒットすると、同じ番組が毎朝送られてくるのは、ちょっと問題だな~。
この番組は除外するって機能が必要かも…。
もしくは、今日・明日・明後日の3日分だけとか限定して同じ番組が送られてきても、最大3回までになるようにするとか?