CodeIgniterの学習 62 - ヤフーの画像検索apiと形態素解析apiを組み合わせてお遊びアプリを作る-その7(現在のコード完成度85%)
CodeIgniterでお遊びアプリを作る7回目。
今回で終わらそうと思っていたけど、終わらなかったのでまだ引っ張る。
残務整理っぽくてテンション低めなのだ。
今日は、前回のエントリーでおさらいした、未完了項目の
4.未 検索履歴を表示する。検索履歴はユーザー毎ではなく、全員と共有。
6.途 1-5の繰り返し。
9.途 検索キーワード・結果の保存(キャッシュ)と表示機能(mysql使用)
のfixをする。
残りの
17.未 検索履歴と接続ログを保存する。
18.未 検索回数のカウンターを設置する。
24.未 ロボットが勝手にカウンターを回さないように、ロボット避けをする。
は次回にまわす。
現在の画面
こんなかんじ。みんなの最新検索キーワードを追加。
他の人の検索内容も載ってくる(最新50件)ので、
エロいキーワードばっかりになる可能性が大なので、機能としては微妙。
検索キーワード履歴(カウンター)保存用テーブルを作る
てきとう。検索する毎にレコードが増えるのは、ここでの用途では微妙なので、
同一の検索パターン(API接続先のURLが同一の時)は、
レコードを追加せずに、カウンターと更新日時のみ更新するようにした。
#検索キーワード履歴(カウンター)保存用テーブル CREATE TABLE `yaimg_keyword` ( `cache_key` varchar(64) NOT NULL, #キャッシュ検索キー 必須 `cache_name` varchar(64) DEFAULT NULL, #キャッシュ名 任意 `keyword` TEXT DEFAULT NULL, #キーワード 任意 `url` TEXT DEFAULT NULL, #API接続先のURL `search_count` int(10) unsigned NOT NULL DEFAULT 1, #検索回数 `del_flg` int(1) unsigned DEFAULT 0, `created` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `modified` timestamp NULL , PRIMARY KEY (`cache_key`), KEY `idx1_yaimg_keyword` (`cache_key`,`del_flg`), KEY `idx2_yaimg_keyword` (`modified`,`del_flg`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
- cache_keyは、キャッシュ用テーブルのcache_keyと同一(このテーブルではこれをプライマリーキーにしている。)
- keywordが検索キーワード
- urlがapiの接続先urlだが、これにはAPIのアプリケーションID(appid)も含まれているので、外部表示用に使うとAPPIDが漏れてしまうので、ビューで使わないように注意。)
- search_countは、同一cache_keyの検索時にカウントアップしていく。(人気のキーワード抽出機能みたいな拡張をするときに使う)
コード
今日も全部貼っておく。(変更ソースは、コントローラとモデルとビューと設定ファイル)1:コントローラ… application/controllers/yaimg.php
keyword_historyをビューに渡すところを追加。
<?php if ( !defined( 'BASEPATH' ) ) exit( 'No direct script access allowed' ); /** * Yahooの画像検索APIを使ってみるテスト */ /** * * @author : dix3 * @link http://d.hatena.ne.jp/dix3/ */ class Yaimg extends Controller { // コンストラクタ function Yaimg() { parent :: Controller(); //親のコンストラクタを呼ぶ //$this -> load -> helper( array( 'form', 'html', 'text', 'string' ) ); //ヘルパのロード $this -> load -> helper( array( 'url','form', 'html', 'text', 'string','date' ) ); //ヘルパのロード $this -> load -> library( 'session' ); //セッションライブラリのロード $this -> load -> library( 'form_validation' ); //フォームバリデーションライブラリのロード $this -> load -> library( 'lib_yaimg' ); //自作ライブラリのロード $this -> load -> model( 'Mod_yaimg' ); //モデルのロード } // デフォルトインデックス function index( $offset_num = 0 ) { $this -> session -> set_userdata( 'yaimg', array() ); $this -> page( $offset_num ); } function page( $offset_num = 0 ) // オフセットだけ引数で渡す。キーワードはPOSTデータまたはセッションデータに限定 { $data = array(); //ビュー側に渡す配列 $data = $this -> Mod_yaimg -> set_init_data(); //画面に固定値のデータをセット $data['urchin_tag'] = ( $this -> Mod_yaimg -> get( 'uacct_no' ) ) ? $this -> load -> view( 'urchin_tag', array( 'uacct_no' => $this -> Mod_yaimg -> get( 'uacct_no' ) ) , true ) : '' ; //UACCT_NOをurchinタグのビューに渡す $data['result'] = array( 'body' => array(), 'msg' => '', 'page_link' => '', 'sentence' => array() ,'keyword_history' =>array()); //検索結果の初期値 // バリデーションのルール設定 $this -> form_validation -> set_rules( 'keyword', $this -> Mod_yaimg -> get( 'input_keyword_value' ), 'trim|required|min_length[2]|max_length[64]|xss_clean' ); $keyword = $this -> _get_keyword(); //検索キーワードの取得(POST経由又はセッション経由) if ( $keyword ) { $data['keyword'] = $keyword; $this -> session -> set_userdata( 'yaimg', array( 'keyword' => $keyword ) ); //セッションに検索キーワードを保持 // apiに接続して結果を取得する if ( $this -> Mod_yaimg -> query_api( array( 'keyword' => $keyword , 'offset_num' => $offset_num ) ) ) { $ir = $this -> Mod_yaimg -> get( 'image_result' ); $data['result']['body'] = $ir['result']; //結果データ $data['result']['page_link'] = $this -> _make_page_link( $ir['attributes'] ); //ページリンクタグの生成 $data['result']['keyword_history'] = $ir['keyword_history']; //キーワード検索履歴 $data['result']['msg'] = $this -> _make_info_label( $ir['attributes'] ); //件数情報表示 if ( $this -> Mod_yaimg -> get( 'parse_api_status' ) ) { $pr = $this -> Mod_yaimg -> get( 'parse_result' ); //形態素解析結果の取得 $data['result']['sentence'] = $pr; } }else { $data['result']['msg'] = $this -> Mod_yaimg -> get( 'msg_no_result' ); } }else { $this -> session -> set_userdata( 'yaimg', array() ); } // ビューにデータを渡す $this -> load -> view( 'yaimg_index', $data ); } // 検索キーワードの取得 function _get_keyword() { $keyword = ''; // post時とページネーション時の振り分け if ( $this -> input -> post( 'submit' ) ) { // バリデーション処理の実行 if ( ( TRUE === $this -> form_validation -> run() ) && ( $this -> input -> post( 'keyword', true ) !== $this -> Mod_yaimg -> get( 'default_keyword' ) ) ) { $keyword = $this -> input -> post( 'keyword', true ); } }else { if ( $sess_data = $this -> session -> userdata( 'yaimg' ) ) { if ( isset( $sess_data['keyword'] ) ) { // セッションにキーワードがセットされている $keyword = $this -> input -> xss_clean( $sess_data['keyword'] ); } } } return $keyword; } // ページリンクタグの生成 function _make_page_link( $attributes ) { // ページネーションライブラリのロード $this -> load -> library( 'pagination' ); $config['base_url'] = '/yaimg/page/'; $total_rows = ( int ) $attributes['total']; // 件数制限 $total_rows = ( ( int ) $this -> Mod_yaimg -> get( 'image_api_max_rows' ) <= $total_rows ) ? ( int ) $this -> Mod_yaimg -> get( 'image_api_max_rows' ) : $total_rows; $config['total_rows'] = $total_rows ; $config['per_page'] = ( int ) $this -> Mod_yaimg -> get( 'image_api_results_count' ); $config['num_links'] = $this -> Mod_yaimg -> get( 'num_links' ); $config['first_link'] = $this -> Mod_yaimg -> get( 'first_link' ); $config['last_link'] = $this -> Mod_yaimg -> get( 'last_link' ); $config['prev_link'] = $this -> Mod_yaimg -> get( 'prev_link' ); $config['next_link'] = $this -> Mod_yaimg -> get( 'next_link' ); $this -> pagination -> initialize( $config ); return $this -> pagination -> create_links(); } // 件数情報表示 function _make_info_label( $attributes ) { $ret = ''; $ilbl = $this -> Mod_yaimg -> get( 'info_label' ); $total = number_format( $attributes['total'] ); $position_h = number_format( $attributes['position'] ); $position_f = number_format( ( int )$attributes['position'] + ( int ) $attributes['count'] -1 ) ; $ret = sprintf( $ilbl, $total, $position_h, $position_f ) ; //総件数情報 if ( ( int )$this -> Mod_yaimg -> get( 'image_api_max_rows' ) <= ( int ) $attributes['total'] ) { $ret .= $this -> Mod_yaimg -> get( 'info_label_notice' ); //件数制限のメッセージ } return $ret; } } /** * End of file yaimg.php */ /** * Location: ./application/controllers/yaimg.php */ ?>
2:ビュー… application/views/yaimg_index.php
keyword_historyのsubmitボタン(見た目はリンク)を追加。(submitボタンなのは、post渡しで済ませるための逃げ。)
あと、ヤフーのAPI使用時のクレジット表記を追加。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja"> <head> <title><?= $svs_title ?></title> <?= meta('Content-Type','text/html; charset=utf-8','http-equiv') ?> <?= meta('Content-language',$header_lang,'http-equiv') ?> <?= meta('Distribution','Global') ?> <?= meta('Author','dix3') ?> <?= meta('Robots','index,nofollow') ?> <?= $header_js ?> <?= $header_css ?> </head> <body> <div> <?= heading($svs_description,3) ?> <?= form_open('yaimg') ?> <?= form_input(array('name'=>'keyword','style'=>'width:250px;','onclick'=>"javascript:this.value='';"),set_value('keyword',$keyword)) ?> <?= form_submit('submit',$submit_button_value) ?> <?= validation_errors('<div class="error">','</div>'); ?> <?php if($result['msg']){ ?><div class='result_msg'><?= $result['msg'] ?></div><?php } ?> <?= form_close() ?> </div> <hr> <div class='sentence_link'> <div class='bold'> <?php if($result['sentence']){ echo $sentence_link_label; } ?> </div> <div> <?= form_open('yaimg') ?> <?php $cnt = 0; foreach($result['sentence'] as $k => $v){ echo form_submit('keyword',$v['sentence'],"class=\"keyword\" title=\"{$v['count']}\"") . "\n"; $cnt++; } if($cnt){ echo form_hidden('submit','submit'); } ?> <?= form_close() ?> </div> </div> <div class='sentence_link'> <div class='bold'> <?php if($result['keyword_history']){ echo '<hr>'.$keyword_history_label; } ?> </div> <div> <?= form_open('yaimg') ?> <?php $cnt = 0; foreach($result['keyword_history'] as $k => $v){ echo form_submit('keyword',$v['keyword'],"class=\"keyword\"") . "\n"; $cnt++; } if($cnt){ echo form_hidden('submit','submit'); } ?> <?= form_close() ?> </div> </div> <div class='page_link'><?= $result['page_link'] ?></div> <div class='result_body'> <?php foreach($result['body'] as $k => $v){ $img_tag = img(array('src'=>$v['thumbnail']['url'], 'style'=>"width:{$v['thumbnail']['width']};height:{$v['thumbnail']['height']};", 'title'=>form_prep($v['summary'].' '.$v['refererurl'].' '.$v['filesize']), 'alt'=>form_prep($v['title']), )); echo anchor($v['refererurl'],$img_tag,array('target'=>'_blank'))."\n"; } ?> </div> <div class='page_link'><?= $result['page_link'] ?></div> <hr> <?= $api_credit ?> <?= $urchin_tag ?> </body> </html>
3:モデル… application/models/mod_yaimg.php
_insupd_keywordメソッド周りなどを追加。
(mysqlの "ON DUPLICATE KEY UPDATE" 節 は使わず、同一cache_keyの存在チェックを先にしている。)
ちなみに、
$this->db->set('search_count', 'search_count+1' , FALSE);
みたいに第三引数をFALSEにすると、search_count+1が自動エスケープされないでそのまま解釈される。(CodeIgniterのマニュアルを参照)
<?php if ( !defined( 'BASEPATH' ) ) exit( 'No direct script access allowed' ); /** * * @author : dix3 * @link http://d.hatena.ne.jp/dix3/ */ class Mod_yaimg extends Model { var $mod_config; var $image_api_arr = array(); var $parse_api_arr = array(); var $image_result = array(); var $parse_result = array(); var $image_api_status = false; var $parse_api_status = false; var $sentence = ''; var $cache_data_arr = array(); var $search_str = ''; var $keyword_arr = array(); // コンストラクタ function Mod_yaimg() { parent :: Model(); $this -> _init(); //初期化 } // 初期化 function _init() { $this -> load -> helper( 'html' ); //$this -> load -> helper( 'date' ); $this -> load -> helper( 'yaimg' ); // $this -> load -> library( 'curl' ); //curlライブラリのロード $this -> load -> library( 'lib_yaimg' ); $this -> load -> config( 'yaimg_config', true ); $this -> mod_config = $this -> config -> item( 'yaimg_config' ); } // 画面に固定値のデータをセット function set_init_data() { $arr = array( 'svs_title' , 'svs_description' , 'header_lang' , 'header_js' , 'header_css' , 'input_keyword_value', 'keyword' , 'submit_button_value' , 'sentence_link_label' , 'keyword_history_label', 'api_credit', ); $data = array(); foreach( $arr as $v ) { $data[$v] = $this -> get( $v ); } return $data; } // 値取得 function get( $v ) { switch ( $v ) { case 'header_js': return header_js( $this -> _get_mod_config( $v ) ); //yaimgヘルパのheader_jsタグ break; case 'header_css': return link_tag( $this -> _get_mod_config( $v ) ); //htmlヘルパのlink_tag break; case 'image_api_arr': return $this -> image_api_arr; //画像取得結果の配列 break; case 'parse_api_arr': return $this -> parse_api_arr; //形態素解析取得結果の配列 break; case 'image_result': return $this -> image_result; //画像取得結果を扱いやすい形に変換した配列 break; case 'parse_result': return $this -> parse_result; //形態素解析結果を扱いやすい形に変換した配列 break; case 'image_api_status': return $this -> image_api_status; //画像検索結果の取得件数有り真偽 break; case 'parse_api_status': return $this -> parse_api_status; // 形態素解析検索結果の取得件数有り真偽 break; case 'sentence': return $this -> sentence; // 形態素解析用文字列 break; case 'cache_data_arr': return $this -> cache_data_arr; //キャッシュ用データ配列 break; case 'keyword_arr': return $this -> keyword_arr; //検索履歴用配列 break; case 'search_str': return $this -> search_str; break; case 'keyword_history': return $this -> _get_keyword_history(); //検索履歴の取得 break; default: return $this -> _get_mod_config( $v ); //configファイルの設定値 break; } } // configファイルから設定値を読みとる function _get_mod_config( $v ) { $v = strtoupper( $v ); return isset( $this -> mod_config[$v] ) ? $this -> mod_config[$v] : ''; } // 画像取得結果の配列をセット function _set_image_api_arr( $arr = array() ) { if ( is_array( $arr ) ) { $this -> image_api_arr = $arr; } } // 形態素解析取得結果の配列をセット function _set_parse_api_arr( $arr = array() ) { if ( is_array( $arr ) ) { $this -> parse_api_arr = $arr; } } // 画像検索結果の戻り値を扱いやすい形に加工してセットする function _set_image_result() { $ret = false; $arr = $this -> get( 'image_api_arr' ); if ( isset( $arr['@attributes'] ) ) { $data['attributes']['total'] = $arr['@attributes']['totalresultsavailable']; $data['attributes']['count'] = $arr['@attributes']['totalresultsreturned']; $data['attributes']['position'] = $arr['@attributes']['firstresultposition']; $data['result'] = isset( $arr['result'] ) ? $arr['result'] : array(); if ( ( int ) $data['attributes']['total'] > 0 ) { $this -> _set_sentence(); //形態素解析用文字列のセット $this -> _set_image_api_status( TRUE ); //取得件数有り真偽 $this -> _insert_cache_tbl( 'yaimg_image_api' ); //DBにキャッシュするべき正常なデータがあれば、ここでDBに格納する $ret = true; }else { $this -> _set_image_api_status( FALSE ); //取得件数有り真偽 } }else { $data['attributes']['total'] = ''; $data['attributes']['count'] = ''; $data['attributes']['position'] = ''; $data['result'] = array(); $this -> _set_image_api_status( FALSE ); //取得件数有り真偽 } $data['keyword_history'] = $this->get('keyword_history');//検索履歴の取得 $this -> image_result = $data; return $ret; } // 形態素解析結果の戻り値を扱いやすい形に加工してセットする function _set_parse_result() { $ret = false; $arr = $this -> get( 'parse_api_arr' ); $data = array(); if ( isset( $arr['uniq_result']['word_list']['word'] ) ) { $regexp_ng1 = '#' . $this -> get( 'ng_parse_word1' ) . '#iu'; $w = str_replace( ',', '|', rtrim( trim( $this -> get( 'ng_parse_word2' ), ',' ) ) ) ; $regexp_ng2 = "#^({$w})$#iu"; foreach( ( array ) $arr['uniq_result']['word_list']['word'] as $v ) { if ( !isset( $v['surface'] ) ) { continue; } if ( mb_strlen( $v['surface'] ) < 2 ) { continue; //1文字単語を除外 } if ( preg_match( $regexp_ng1, $v['surface'] ) ) { continue; //不要な単語を除外 } if ( preg_match( $regexp_ng2, $v['surface'] ) ) { continue; //不要な単語を除外 } $data[] = array( "sentence" => strip_tags( $v['surface'] ), "count" => $v['count'], "pos" => $v['pos'] ); $ret = true; } if ( $ret ) { $this -> _set_parse_api_status( TRUE ); //取得件数有り真偽 $this -> _insert_cache_tbl( 'yaimg_parse_api' ); //DBにキャッシュするべき正常なデータであれば、ここでDBに格納する } } $this -> parse_result = $data; return $ret; } // DBにAPI検索結果のキャッシュを保存する function _insert_cache_tbl( $cache_name ) { if ( $this -> get( 'cache_use' ) ) { $arr = $this -> get( 'cache_data_arr' ); if ( isset( $arr[$cache_name] ) && is_array( $arr[$cache_name] ) && $arr[$cache_name] ) { foreach( $arr[$cache_name] as $data ) { $this -> db -> insert( 'yaimg_cache_tbl', $data ); } } } } // DBに検索履歴を保存する(重複する条件はカウンタと更新日時のみ更新) function _insupd_keyword( $cache_name ) { $arr = $this -> get( 'keyword_arr' ); if ( isset( $arr[$cache_name] ) && is_array( $arr[$cache_name] ) && $arr[$cache_name] ) { foreach( $arr[$cache_name] as $data ) { $this->db->where('cache_key', $data['cache_key']); $this->db->from('yaimg_keyword'); //$now = timestamp_to_mysqldatetime(time()); 本当はMY_date_helperを使っているが $now = date("Y-m-d H:i:s",time()); if($this->db->count_all_results()){ foreach(array('cache_name','keyword','url') as $v){ $this->db->set($v, $data[$v]); } $this->db->set('search_count', 'search_count+1' , FALSE); $this->db->set('modified', $now); $this->db->where('cache_key', $data['cache_key']); $this -> db -> update( 'yaimg_keyword'); }else{ foreach(array('cache_name','keyword','url') as $v){ $this->db->set($v, $data[$v]); } $this->db->set('created', $now); $this->db->set('modified', $now); $this -> db -> insert( 'yaimg_keyword', $data ); } } } } // 形態素解析用文字列のセット function _set_sentence() { $arr = $this -> get( 'image_api_arr' ); $s_arr = array(); // var_dump($arr); if ( isset( $arr['result'] ) ) { foreach( ( array ) $arr['result'] as $k => $v ) { if ( isset( $v['summary'] ) && $v['summary'] ) { $s_arr[] = $v['summary']; } } // var_dump($s_arr); $str = implode( ',', $s_arr ); } $this -> sentence = $str; } // 画像検索結果の取得件数有り真偽をセット function _set_image_api_status( $flg ) { $this -> image_api_status = ( $flg ) ? TRUE : FALSE ; } // 形態素解析検索結果の取得件数有り真偽をセット function _set_parse_api_status( $flg ) { $this -> parse_api_status = ( $flg ) ? TRUE : FALSE ; } // DBにキャッシュデータを保存する予約 function _set_cache_data_arr( $cache_name , $arr = array() ) { if ( $cache_name && is_array( $arr ) && $arr ) { $this -> cache_data_arr[$cache_name][] = $arr; //複数のキャッシュデータを保持する } } // DBに検索履歴データを保存する予約 function _set_keyword_arr( $cache_name , $arr = array() ) { if ( $cache_name && is_array( $arr ) && $arr ) { $this -> keyword_arr[$cache_name][] = $arr; //複数のキャッシュデータを保持する } } //検索語句 function _set_search_str($str){ $this -> search_str = $str; } // apiに接続して結果を取得する function query_api( $params = array() ) { $keyword = isset( $params['keyword'] ) ? $this -> input -> xss_clean( strip_tags( $params['keyword'] ) ) : ''; $offset_num = isset( $params['offset_num'] ) ? $this -> input -> xss_clean( strip_tags( $params['offset_num'] ) ) : ''; if ( $keyword ) { $this -> _query_image_api( $keyword , $offset_num ); //画像検索apiに接続 } $sentence = $this -> get( 'sentence' ); //形態素解析用文字列の取得 if ( $sentence ) { $this -> _query_parse_api( $sentence ); //形態素解析apiに接続 } return $this -> get( 'image_api_status' ); //画像が見つかったらtrue } // 画像検索apiに接続して結果を取得する function _query_image_api( $keyword = '', $offset_num = 0 ) { if ( !( $url = $this -> _get_ya_url( 'image_api', $keyword , $offset_num ) ) ) { return false; } $data = $this -> _get_contents( 'yaimg_image_api', $url ); //キャッシュまたはURLからデータ取得 if ( $data ) { $obj = @simplexml_load_string( $data ); //xmlのパース // 取り回しが面倒なので配列に変換 $this -> _set_image_api_arr( $this -> _convert_obj_to_arr( $obj ) ); return $this -> _set_image_result(); // 画像検索結果の戻り値を扱いやすい形に加工してセットする }else { return false; } } // キャッシュまたはURLからデータ取得 function _get_contents( $cache_name, $url ) { // キャッシュからデータ取得を試みる $cache_key = md5( $cache_name . $url ); //cache_name+urlのmd5を検索キーとする。別にSALTを入れなくてもいいよね $ret_data = $this -> _get_contents_by_cache( $cache_name, $cache_key, $url ); // 適切なキャッシュデータがDB上に無いときは、外部に接続 if ( !$ret_data ) { usleep( 100000 ); //連続してつながないように微妙にウエイト // $ret_data = $this -> curl -> get( $url ); //curlライブラリ経由の場合 $ret_data = @file_get_contents( $url ); // 本当はステータスを取りたいけど if ( $ret_data ) { // DBにキャッシュデータを保存する予約 // (正常なデータのみキャッシュさせるのでこの段階ではDBに保存はしない) $this -> _set_cache_data_arr( $cache_name, array( 'cache_name' => $cache_name, 'cache_key' => $cache_key, 'cache_data' => serialize( $ret_data ), 'expire_time' => time() + ( int ) $this -> get( 'cache_expire' ) ) ); } } return $ret_data; } // キャッシュからデータ取得を試みる function _get_contents_by_cache( $cache_name, $cache_key, $url ) { $ret_data = NULL; if ( !$this -> get( 'cache_use' ) ) { return $ret_data; } //検索履歴用データのセット $this->_set_keyword_arr($cache_name,array('cache_name'=>$cache_name, 'cache_key'=>$cache_key, 'url'=>$url ,'keyword'=>$this->get('search_str'))); //検索履歴の更新 $this->_insupd_keyword($cache_name); // ----------------------- // API接続回数の削減 // ----------------------- // APIには接続回数の上限があるため、 // APIに接続する前に、DB上のキャッシュ用テーブルからレコードを探して外部接続を減らす // さらに負荷を減らす為にうまくデータベースキャッシュクラスを使っている $this -> db -> where( 'cache_key', $cache_key ); $this -> db -> where( 'del_flg', 0 ); $this -> db -> order_by( 'id', 'DESC' ); $this -> db -> limit( 1 ); $this -> db -> cache_on(); //データベースキャッシュクラスを併用する $query = $this -> db -> get( 'yaimg_cache_tbl' ); //TODO:汎用ライブラリ化して他の処理でも使えるように改良したいがとりあえず if ( $query -> num_rows() ) { $r = $query -> result_array(); $id = $r[0]['id']; $cache_data = $r[0]['cache_data']; //キャッシュデータ $expire_time = $r[0]['expire_time']; //有効期限(unixtime) $now = time(); if ( $now < $expire_time ) { // 有効期限内の場合 $ret_data = unserialize( $cache_data ); // var_dump( 'キャッシュ有り' ); }else { // var_dump( '期限切れファイルキャッシュ削除' ); $this -> _delete_dbcache_by_sql( $this -> db -> last_query() ); //データベースキャッシュの特定のファイルを削除する $this -> db -> delete( 'yaimg_cache_tbl', array( 'expire_time < ' => "$now" ) ); //期限切れのレコードを全削除 } }else { // レコード0件の時のデータベースキャッシュは次回select時に邪魔になるので削除する $this -> _delete_dbcache_by_sql( $this -> db -> last_query() ); //データベースキャッシュの特定のファイルを削除する // var_dump( 'レコード0件のファイルキャッシュ削除' ); } $this -> db -> cache_off(); //データベースキャッシュクラスをOFFにする return $ret_data; } // データベースキャッシュクラス使用時のキャッシュファイルの個別削除 // 本当は DB_cache.phpを拡張したいところが、取りあえずモデル内に設置しておく function _delete_dbcache_by_sql( $sql ) { if ( !$this -> db -> cachedir ) { // var_dump('application/config/database.phpのcachedirに設定がない'); return false; } if ( !realpath( $this -> db -> cachedir ) ) { return false; } if ( !$sql ) { return false; } $segment_one = ( $this -> uri -> segment( 1 ) == FALSE ) ? 'default' : $this -> uri -> segment( 1 ); $segment_two = ( $this -> uri -> segment( 2 ) == FALSE ) ? 'index' : $this -> uri -> segment( 2 ); $filepath = $this -> db -> cachedir . '/' . $segment_one . '+' . $segment_two . '/' . md5( $sql ); //DB_cache.phpの仕様 if ( realpath( $filepath ) ) { // var_dump('キャッシュファイルの個別削除実行 path:'.realpath( $filepath )); @unlink( realpath( $filepath ) ); return true; } return false; } // 形態素解析apiに接続して単語をばらした結果を配列で取得する function _query_parse_api( $sentence = '' ) { if ( !( $url = $this -> _get_ya_url( 'parse_api', $sentence ) ) ) { return false; } $data = $this -> _get_contents( 'yaimg_parse_api', $url ); //キャッシュまたはURLからデータ取得 if ( $data ) { $obj = @simplexml_load_string( $data ); //xmlのパース // 取り回しが面倒なので配列に変換 $this -> _set_parse_api_arr( $this -> _convert_obj_to_arr( $obj ) ); return $this -> _set_parse_result(); // 形態素解析結果の戻り値を扱いやすい形に加工してセットする }else { return false; } } // 面倒なのでオブジェクトを再帰的に配列に変換 function _convert_obj_to_arr( $p ) { if ( is_object( $p ) ) { $ret = array(); $arr = get_object_vars( $p ); foreach( $arr as $k => $v ) { $key = strtolower( $k ); //キーは小文字で統一 if ( empty( $v ) ) { $ret[$key] = ''; }else { $ret[$key] = $this -> _convert_obj_to_arr( $v ); } } }elseif ( is_array( $p ) ) { $ret = array(); foreach( $p as $k => $v ) { $key = strtolower( $k ); //キーは小文字で統一 if ( empty( $v ) ) { $ret[$key] = ''; }else { $ret[$key] = $this -> _convert_obj_to_arr( $v ); } } }else { $ret = $p; } return $ret; } // URLの組み立て function _get_ya_url( $ptn, $str , $offset_num = 0 ) { if ( !$str ) { return ''; } $str = $this -> input -> xss_clean( strip_tags( $str ) ); $this->_set_search_str($str);//検索履歴保存用 $appid = $this -> get( 'appid' ); switch ( $ptn ) { case 'parse_api':// 形態素解析用URLの組み立て // アルファベットと数字のみの文字列をあらかじめ除外 $str = preg_replace( '#[a-zA-Z0-9]+#iu', ' ', $str ); $enc_str = urlencode( mb_substr( $str , 0, 960 ) ); //適当に短くしないとリクエストが長すぎて蹴られる $url = $this -> get( 'parse_api_url' ); $url = str_replace( '%appid%', $appid, $url ); $url = str_replace( '%sentence%', $enc_str, $url ); return $url; break; case 'image_api':// 画像検索用URLの組み立て $enc_str = urlencode( $str ); $url = $this -> get( 'image_api_url' ); $url = str_replace( '%appid%', $appid, $url ); $url = str_replace( '%keyword%', $enc_str, $url ); $site_lock_url_list = $this -> get( 'site_lock_url_list' ); //検索対象のURLを限定 if ( $site_lock_url_list ) { $s_arr = explode( ',', trim( str_replace( ' ', '', $site_lock_url_list ) ) ); $image_api_site_lock_url = '&site=' . implode( '&site=', $s_arr ); $url = str_replace( '%image_api_site_lock_url%', $image_api_site_lock_url, $url ); } $arr = array( 'image_api_adult_ok', 'image_api_similar_ok', 'image_api_language', 'image_api_country', 'image_api_results_count', ); foreach( $arr as $v ) { $url = str_replace( "%{$v}%", $this -> get( $v ), $url ); } // 取得開始カウントは正の整数のみ if ( !preg_match( '#^\d+$#', $offset_num ) || ( ( int ) $offset_num < 1 ) ) { $start_num = ( int ) $this -> get( 'image_api_start_num' ); }else { $start_num = ( int ) $offset_num; //パラメータstartの調整 } $url = str_replace( "%image_api_startcount_num%", $start_num, $url ); // echo($url); return $url; break; default: return ''; break; } } //検索履歴の取得 function _get_keyword_history($num=50){ $ret_arr = array(); $this->db->select('keyword'); $this->db->where('del_flg',0); $this->db->where('cache_name','yaimg_image_api'); $this->db->order_by('modified','DESC'); $this -> db -> limit( (int) $num ); $this ->db->distinct();//遅いかなー $query = $this -> db -> get( 'yaimg_keyword' ); if ( $query -> num_rows() ) { $ret_arr = $query -> result_array(); } return $ret_arr; } } //endofclass // yaimg_cache_tblのddl /** * #キャッシュデータ保存用テーブル * CREATE TABLE `yaimg_cache_tbl` ( * `id` int(10) unsigned NOT NULL auto_increment, * `cache_key` varchar(64) NOT NULL, #キャッシュ検索キー 必須 * `cache_name` varchar(64) DEFAULT NULL, #キャッシュ名 任意 * `cache_data` MEDIUMTEXT NOT NULL, #キャッシュデータ 必須 * `expire_time` varchar(11) DEFAULT NULL, #有効期限 unixtime 任意 * `del_flg` int(1) unsigned DEFAULT 0, * `created` timestamp NULL DEFAULT CURRENT_TIMESTAMP, * `modified` timestamp NULL , * PRIMARY KEY (`id`), * KEY `idx1_yaimg_cache_tbl` (`id`,`del_flg`), * KEY `idx2_yaimg_cache_tbl` (`cache_key`,`del_flg`), * KEY `idx3_yaimg_cache_tbl` (`expire_time`) * ) ENGINE=MyISAM DEFAULT CHARSET=utf8; * * * #検索キーワード履歴(カウンター)保存用テーブル * CREATE TABLE `yaimg_keyword` ( * `cache_key` varchar(64) NOT NULL, #キャッシュ検索キー 必須 * `cache_name` varchar(64) DEFAULT NULL, #キャッシュ名 任意 * `keyword` TEXT DEFAULT NULL, #キーワード 任意 * `url` TEXT DEFAULT NULL, #API接続先のURL * `search_count` int(10) unsigned NOT NULL DEFAULT 1, #検索回数 * `del_flg` int(1) unsigned DEFAULT 0, * `created` timestamp NULL DEFAULT CURRENT_TIMESTAMP, * `modified` timestamp NULL , * PRIMARY KEY (`cache_key`), * KEY `idx1_yaimg_keyword` (`cache_key`,`del_flg`), * KEY `idx2_yaimg_keyword` (`modified`,`del_flg`) * ) ENGINE=MyISAM DEFAULT CHARSET=utf8; */ /** * End of file mod_yaimg.php */ /** * Location: ./application/models/mod_yaimg.php */ ?>
4:ライブラリ… application/libraries/lib_yaimg.php
前回までと変わらず。モデルとライブラリの分離をやる必要があるか微妙なので、いらなくなりそう。
<?php if ( !defined( 'BASEPATH' ) ) exit( 'No direct script access allowed' ); /* * @author: dix3 * @link : http://d.hatena.ne.jp/dix3/ */ class Lib_yaimg { function Lib_yaimg() { } } //endofclass /** * End of file lib_yaimg.php */ /** * Location: ./application/libraries/lib_yaimg.php */ ?>
5:ヘルパー… application/helpers/yaimg_helper.php
前回までと変わらず。ヘッダのjavascriptを読みこむ用。<?php if ( !defined( 'BASEPATH' ) ) exit( 'No direct script access allowed' ); /* * @author: dix3 * @link : http://d.hatena.ne.jp/dix3/ */ if ( ! function_exists( 'header_js' ) ) { // scriptの読み込みタグ生成 // 例: header_js('js/hogehoge.js');で // <script src="js/hogehoge.js" type="text/javascript"></script> // を作る function header_js( $src_str = '', $charset = '' ) { $ret = ''; if ( $src_str ) { if ( $charset ) { $cstr = ' charset="' . $charset . '" ' ; }else { $cstr = '' ; } $ret .= '<script language="javascript" type="text/javascript" src="' . $src_str . '"' . $cstr . "></script>\n"; } return $ret; } } /* End of file yaimg_helper.php */ /* Location: ./application/helpers/yaimg_helper.php */ ?>
6:設定ファイル… application/config/yaimg.php
KEYWORD_HISTORY_LABELとAPI_CREDITを追加。APPIDは各自が取得してね。
<?php if ( ! defined( 'BASEPATH' ) ) exit( 'No direct script access allowed' ); /** * * @author : dix3 * @link : http://d.hatena.ne.jp/dix3/ */ $config['SVS_TITLE'] = 'ヤフー画像検索 WEBAPIテスト'; //タイトル $config['SVS_NAME'] = 'yaimg_search'; //サービス名 $config['SVS_DESCRIPTION'] = 'ヤフー画像検索と形態素解析の実験'; //ページタイトル $config['HEADER_LANG'] = 'ja'; //Content-language $config['HEADER_JS'] = 'js/yaimg.js'; //javascriptの相対パス $config['HEADER_CSS'] = 'css/yaimg.css'; //cssの相対パス $config['INPUT_KEYWORD_VALUE'] = 'キーワード'; //キーワード欄の名称 $config['SUBMIT_BUTTON_VALUE'] = 'ぽちっとな!画像検索'; //サブミットボタン名 $config['KEYWORD'] = 'なんかいれて'; //キーワード欄のデフォルト値 $config['SENTENCE_LINK_LABEL'] = '関連キーワード:'; $config['KEYWORD_HISTORY_LABEL'] = 'みんなの最新検索キーワード:'; $config['INFO_LABEL'] = '総件数:%s件 %s件目 〜 %s件目を表示'; //情報用ラベル $config['INFO_LABEL_NOTICE'] = ' (検索結果の表示は999件が上限です。)'; //情報用ラベル $config['FIRST_LINK'] = '最初のページ'; //ページネーション用 $config['LAST_LINK'] = '最後のページ'; //ページネーション用 $config['PREV_LINK'] = '<前へ '; //ページネーション用 $config['NEXT_LINK'] = '>次へ'; //ページネーション用 $config['NUM_LINKS'] = '5'; //ページネーション用 $config['CACHE_USE'] = '1' ;//API負荷削減のためにキャッシュ機能(DB内にキャッシュ保存+ファイルキャッシュ併用)を使うか 使用:1 不使用:0 $config['CACHE_EXPIRE'] = '43200';//同じ検索内容のキャッシュ有効期間(43200秒すなわち12時間) $config['API_CREDIT'] = "<!-- Begin Yahoo! JAPAN Web Services Attribution Snippet -->\n<a href=\"http://developer.yahoo.co.jp/about\">\n<img src=\"http://i.yimg.jp/images/yjdn/yjdn_attbtn2_105_17.gif\" width=\"105\" height=\"17\" title=\"Webサービス by Yahoo! JAPAN\" alt=\"Webサービス by Yahoo! JAPAN\" border=\"0\" style=\"margin:15px 15px 15px 15px\"></a>\n<!-- End Yahoo! JAPAN Web Services Attribution Snippet -->\n";//クレジット表記 $config['UACCT_NO'] = ''; //google-analyticsのurchinのタグ // yahooのアプリケーションID appid(自分で取得する) $config['APPID'] = ''; //yahooのappid(自分でとる) // API接続URLのテンプレート // yahooの形態素解析apiのURL #$config['PARSE_API_URL'] = 'http://api.jlp.yahoo.co.jp/MAService/V1/parse?appid=%appid%&results=uniq&uniq_filter=9&sentence=%sentence%'; $config['PARSE_API_URL'] = 'http://jlp.yahooapis.jp/MAService/V1/parse?appid=%appid%&results=uniq&uniq_filter=9&sentence=%sentence%'; // yahooの画像検索apiのURL #$config['IMAGE_API_URL'] = 'http://api.search.yahoo.co.jp/ImageSearchService/V1/imageSearch?appid=%appid%&query=%keyword%&adult_ok=%image_api_adult_ok%&similar_ok=%image_api_similar_ok%&language=%image_api_language%&country=%image_api_country%%image_api_site_lock_url%&results=%image_api_results_count%&start=%image_api_startcount_num%'; $config['IMAGE_API_URL'] = 'http://search.yahooapis.jp/ImageSearchService/V1/imageSearch?appid=%appid%&query=%keyword%&adult_ok=%image_api_adult_ok%&similar_ok=%image_api_similar_ok%&language=%image_api_language%&country=%image_api_country%%image_api_site_lock_url%&results=%image_api_results_count%&start=%image_api_startcount_num%'; // 画像検索APIのパラメータ $config['IMAGE_API_ADULT_OK'] = '1'; //アダルト OK:1,NG:0 $config['IMAGE_API_SIMILAR_OK'] = '1'; //SIMILAR OK:1,NG:0 $config['IMAGE_API_LANGUAGE'] = 'ja'; //言語 ja $config['IMAGE_API_COUNTRY'] = 'ja'; //国 ja $config['IMAGE_API_RESULTS_COUNT'] = '50'; //一回の問い合わせのデータ取得件数 $config['IMAGE_API_START_NUM'] = '1'; //検索結果のn番目から表示 $config['IMAGE_API_MAX_ROWS'] = '1000'; //1000件を超えられないAPIの仕様 $config['SITE_LOCK_URL_LIST'] = ''; //画像検索を特定のサイト内に限定するときに指定 // $config['SITE_LOCK_URL_LIST'] = 'hatena.ne.jp,example.com';//画像検索を特定のサイト内に限定するときに指定。カンマ区切りで複数指定可 // 形態素解析での抽出対象外ワード、正規表現で指定 $config['NG_PARSE_WORD1'] = '[-+_0-9a-zA-Z\s0123456789]+';//数字のみアルファベットのみを除外 // 形態素解析での抽出対象外ワード、カンマ区切りで指定 $config['NG_PARSE_WORD2'] = '上記,名前,名無し,投稿,片手,クリック,サイト,ジャンプ,リンク,これ,それ,あれ,こと,どれ,ここ,そこ,こちら,先週,今週,来週,版,先月,今月,来月'; // 合致したデータが無いときのメッセージ $config['MSG_NO_RESULT'] = '該当するデータは見つかりませんでした。'; /** * End of file yaimg.php */ /** * Location: ./application/config/yaimg.php */ ?>
7:javascriptファイル… ドキュメントルート/js/yaimg.js
前回までと変わらず。(hogeは動作確認用ダミー。)不要になりそう。
//@author : dix3 //@url : http://d.hatena.ne.jp/dix3 var hoge=function(){ try{ window.alert('hoge'); }catch(e){ window.alert(e); } }
8:cssファイル… ドキュメントルート/css/yaimg.css
前回までと変わらず。/* author : dix3 */ /* link : http://d.hatena.ne.jp/dix3/ */ body{ color:#fff; background:#111; } hr { color:#eeeeee; height:1px; } h1,h2,h3{ font-size:1em; } a{ text-decoration: none; } .error{ background:#ff0000; color:#fff; padding:3px; font-weight:bold; display:inline; } .result_msg{ background:#ffd700; color:#111; padding:3px; font-weight:bold; display:inline; } .result_body{ text-decoration: none; margin:20px 0 0 0; padding:5px; } .result_body a:link, .result_body a:visited, .result_body a:active{ color: #111; } .result_body a:hover{ color: #ffd700; } .page_link{ font-size:1.2em; text-align:center; margin:10px 0 10px 0; } .page_link b{ color:#111; background:#ffd700; } .page_link a{ color:#fff; } .page_link a:hover{ color:#ffd700; } .sentence_link input.keyword{ margin:0px; padding:0px; color:#fff; background:#111; font-size:0.875em; border:0; cursor:pointer; } .sentence_link input.keyword:hover{ color:#ffd700; background:#777; } .bold{ font-weight:bold; } img{ border:2px solid #111; } img:hover{ border:2px solid #ffd700; }
9:google-analytics(urchin)用ビューファイル… application/modules/yaimg/views/urchin_tag.php
前回までと変わらず。<script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("<?= $uacct_no ?>"); pageTracker._trackPageview(); </script>
次は残りの
17.未 検索履歴と接続ログを保存する。
18.未 検索回数のカウンターを設置する。
24.未 ロボットが勝手にカウンターを回さないように、ロボット避けをする。
をおこなう。来週くらいかな。
09/02/22 一部修正
- ヘルパのロードに 'url','date'を追加(autoloadで読みこんでいたため)
- cssに img , img : hover 追加