前にホテル検索サイトをじゃらんのAPIで作ったときのメモ。
以前からWebAPIのXMLデータをパースするのがめんどくさくてしょうがなかったんだよね。
で、何とか簡単にパーサを組めないかと思って作ってみたのが「API抽象クラス」。
特にオブジェクト指向とか勉強したわけじゃないので、ぐっちゃりしたコードになってるけどご勘弁を。
仕組み的には抽象クラスにして「XMLパラメータとパーサをくくりだした」だけのものなんだけど、実際に抽象クラスから派生させたクラスにはパラメータ定義とパースのプログラムを書くだけになった。
一つ一つのクラスは小さく見通しの良い物にしようとしてるけど、結局は自分で使うものなのでテキトーな感じになってしまうのは仕方ないね。
抽象クラスの下にあるコードは実際にじゃらんのAPIをパースするクラス。例としてのせておきますね。
◆WebAPI抽象クラス
<?
/*
* APIクラス処理クラス(親クラス)
* DATE : 2008/10/17
*/
// MySQL接続クラス
include_once( dirname( __FILE__ ) . '/MySQL.php' );
abstract class WebApi
{
protected $params; // リクエストパラメータ(連想配列)
protected $url; // パラメータが付与されたURL
protected $xml; // 取得XML
protected $data; // パースされたデータ
protected $max_count; // データの最大件数
public function __construct(){}
/**
リクエストパラメータ配列初期化処理
このメソッドの中でparamsの連想キーを設定する。
*/
abstract protected function init_params();
/**
APIのURL(パラメータ無し)を返す
*/
abstract public function get_url();
/**
XMLをパースする(抽象メソッド)
*/
abstract protected function parse_xml();
/**
リクエストパラメータが存在するか
*/
public function is_param( $param_name )
{
if( isSet( $this->params[ $param_name ] ) ) {
return true;
} else {
return false;
}
}
/**
リクエストパラメータ取得
*/
public function get_param( $param_name )
{
if( $this->is_param( $param_name ) ) {
return $this->params[ $param_name ];
} else {
return false;
}
}
/**
リクエストパラメータ設定
*/
public function set_param( $param_name, $value )
{
if( $this->is_param( $param_name ) ) {
$this->params[ $param_name ] = $value;
return true;
} else {
return false;
}
}
/**
XMLデータ取得
$uri : APIのURI。(終端に記号('?' or '&')を入れないこと。
パース結果を返す。
*/
public function get_xml( $uri )
{
// URL編集
$temp = array();
foreach( $this->params AS $key => $param ) {
if( $param != "" ) {
$temp[] = sprintf( '%s=%s', $key, $param );
}
}
// $uriがすでにパラメータ指定がされているかどうか。
if( substr_count( $uri, '?' ) > 0 ) {
$this->url = sprintf( '%s&%s', $uri, implode( '&', $temp ) );
} else {
$this->url = sprintf( '%s?%s', $uri, implode( '&', $temp ) );
}
$strSql = "SELECT * FROM web_api_result ";
$strSql .= sprintf( "WHERE md5 = '%s' ", md5( $this->url ) );
$con = new DataBaseConnect();
$rec = new ResultSet( $con->executeQuery( $strSql ) );
if( $rec->getRowCount() > 0 ) {
$temp = unserialize( $rec->getField( "serial" ) );
$this->max_count = $temp[ 'max_count' ];
$this->data = $temp[ 'data' ];
} else {
$this->xml = $this->exec_method();
$this->parse_xml();
$this->store_xml();
}
return $this->data;
}
/**
ページタグの取得
*/
public function get_page_tag( $file_name, $current_page, $max_page, $param )
{
if( $current_page == 1 ) {
if( $max_page > 1 ) {
$page_tag = sprintf( '←|%s / %s|<a href="/%s?%s&p=%s">→</a>', $current_page, $max_page, $file_name, $param, ($current_page + 1) );
} else {
$page_tag = sprintf( '←|1 / 1|→' );
}
} else {
if( $current_page >= $max_page ) {
$page_tag = sprintf( '<a href="/%s?%s&p=%s">←</a>|%s / %s|→', $file_name, $param, ($current_page - 1), $current_page, $max_page );
} else {
$page_tag = sprintf( '<a href="/%s?%s&p=%s">←</a>|%s / %s|<a href="/%s?%s&p=%s">→</a>', $file_name, $param, ($current_page - 1), $current_page, $max_page, $file_name, $param, ($current_page + 1) );
}
}
return $page_tag;
}
/**
データベースにXMLを保存
*/
protected function store_xml()
{
// md5用SQL
$sql_md5 = "SELECT * FROM web_api_result ";
$sql_md5 .= sprintf( "WHERE md5 = '%s' ", md5( $this->url ) );
$strSql = "INSERT INTO web_api_result (md5, md5_sql, regist, api_url, xml, serial) VALUES(";
$strSql .= sprintf( "'%s', ", md5( $this->url ) );
$strSql .= sprintf( "'%s', ", md5( $sql_md5 ) );
$strSql .= sprintf( "'%s', ", date( 'Y-m-d' ) );
$strSql .= sprintf( "'%s', ", $this->url );
$strSql .= sprintf( "'%s', ", mysql_real_escape_string( $this->xml ) );
$strSql .= sprintf( "'%s') ", mysql_real_escape_string( serialize( array( 'max_count' => $this->max_count, 'data' => $this->data ) ) ) );
$con = new DataBaseConnect();
$con->execute( $strSql );
}
// URLを確認してPOST or GETを選び実行する
private function exec_method()
{
return $this->get();
}
// パラメータの桁数が256Byte以下の場合はGetメソッドで取得する
private function get()
{
mb_regex_encoding( 'SJIS' );
$temp = mb_convert_encoding( @file_get_contents( $this->url ), 'SJIS', 'UTF-8' );
$temp = mb_ereg_replace( "[■□◆◇●○▲▼△▽☆★♪]+", '', $temp );
return $temp;
}
}
?>
-------------------------------------------------------------------------------------
◆宿データAPI処理クラス
<?
/*
* じゃらん宿データAPI処理クラス
* DATE : 2008/10/18
*/
include_once( dirname( dirname( __FILE__ ) ) . '/config.php' );
include_once( LIB_JARAN_HOTEL );
class JaranHotelAdvance extends WebApi
{
const URI = 'http://jws.jalan.net/APIAdvance/HotelSearch/V1/?key=[アクセスキー]';
const PAGE_PAR_COUNT = 10;
private $response_fields; // レスポンスフィールド定義。
public function __construct()
{
$this->init_params();
}
public function get_url()
{
return self::URI;
}
public function get_page_par_count()
{
return self::PAGE_PAR_COUNT;
}
public function get_max_count()
{
return $this->max_count;
}
protected function init_params()
{
// リクエストパラメータ設定
$this->params[ 'pref' ] = ''; // 都道府県コード
$this->params[ 'l_area' ] = ''; // 大エリアコード(都道府県内を分けたコード)
$this->params[ 's_area' ] = ''; // 小エリアコード(大エリアをさらに分けたコード)
$this->params[ 'h_id' ] = ''; // 宿コード(これを指定すると他すべて無視)
$this->params[ 'h_name' ] = ''; // 宿名
$this->params[ 'h_type' ] = ''; // 宿タイプ(パラメータは仕様書参照)
$this->params[ 'o_id' ] = ''; // 温泉ID
$this->params[ 'x' ] = ''; // 経度
$this->params[ 'y' ] = ''; // 緯度
$this->params[ 'range' ] = ''; // 緯度経度から半径rangeKMの範囲を探す。
$this->params[ 'o_pool' ] = ''; // 屋外プール(0:なしdefault 1:あり)
$this->params[ 'parking' ] = ''; // 無料駐車場(0:なしdefault 1:あり)
$this->params[ 'pub_bath' ] = ''; // 内湯・大浴場(0:なしdefault 1:あり)
$this->params[ 'onsen' ] = ''; // 温泉(0:なしdefault 1:あり)
$this->params[ 'prv_bath' ] = ''; // 貸切風呂(0:なしdefault 1:あり)
$this->params[ 'sauna' ] = ''; // サウナ(0:なしdefault 1:あり)
$this->params[ 'jacz' ] = ''; // ジャグジー(0:なしdefault 1:あり)
$this->params[ 'mssg' ] = ''; // マッサージ(0:なしdefault 1:あり)
$this->params[ 'r_ski' ] = ''; // 貸しスキー(0:なしdefault 1:あり)
$this->params[ 'r_brd' ] = ''; // 貸しスノーボード(0:なしdefault 1:あり)
$this->params[ 'pet' ] = ''; // ペットOK(0:なしdefault 1:あり)
$this->params[ 'esthe' ] = ''; // エステ(0:なしdefault 1:あり)
$this->params[ 'p_pong' ] = ''; // 卓球(0:なしdefault 1:あり)
$this->params[ 'limo' ] = ''; // 送迎(0:なしdefault 1:あり)
$this->params[ 'late_out' ] = ''; // チェックアウト11時以降(0:なしdefault 1:あり)
$this->params[ 'pict_size' ] = '1'; // 画像サイズ(詳細は仕様書)
$this->params[ 'order' ] = ''; // 並び順(0:宿コード順 1:50音順)
$this->params[ 'start' ] = ''; // 何件目から表示するか
$this->params[ 'count' ] = self::PAGE_PAR_COUNT; // 1ページに何件表示するか
$this->params[ 'xml_ptn' ] = '1';
// レスポンスフィールド設定
$this->response_fields[] = 'hotelid';
$this->response_fields[] = 'hotelname';
$this->response_fields[] = 'postcode';
$this->response_fields[] = 'hoteladdress';
$this->response_fields[] = 'region';
$this->response_fields[] = 'prefecture';
$this->response_fields[] = 'largearea';
$this->response_fields[] = 'smallarea';
$this->response_fields[] = 'hoteltype';
$this->response_fields[] = 'hoteldetailurl';
$this->response_fields[] = 'hotelcatchcopy';
$this->response_fields[] = 'hotelcaption';
$this->response_fields[] = 'pictureurl';
$this->response_fields[] = 'picturecaption';
$this->response_fields[] = 'accessinfomation';
$this->response_fields[] = 'checkintime';
$this->response_fields[] = 'checkouttime';
$this->response_fields[] = 'x';
$this->response_fields[] = 'y';
$this->response_fields[] = 'sampleratefrom';
$this->response_fields[] = 'hotelnamekana';
$this->response_fields[] = 'onsenname';
$this->response_fields[] = 'numberofratings';
}
protected function parse_xml()
{
$this->xml = preg_replace( "/\s+/", " ", $this->xml );
$this->data = array();
$ret = preg_match( "@<numberofresults>([0-9]+)</numberofresults>@i", $this->xml, $max_count );
$this->max_count = (int)$max_count[ 1 ];
$ret = preg_match_all( '@<hotel>(.*?)</hotel>@i', $this->xml, $hotels );
foreach( $hotels[ 1 ] AS $hotel ) {
$temp = array();
// レスポンスフィールド設定による読み出し。
foreach( $this->response_fields AS $field ) {
$pattern = sprintf( '@<%s>(.*?)</%s>@i', $field, $field );
$ret = preg_match( $pattern, $hotel, $result );
$temp[ $field ] = $result[ 1 ];
}
// フィールド設定では取れない取得物
// フィールド設定では取れない取得物
$ret = preg_match_all( '@<accessinformation name="(.*?)">(.*?)</accessinformation>@i', $hotel, $accesses );
$temp2 = array();
for( $i = 0 ; $i < count( $accesses[ 1 ] ) ; $i += 1 ) {
$temp2[ $i ] = array( $accesses[ 1 ][ $i ] => $accesses[ 2 ][ $i ] );
}
$temp[ 'accesses' ] = $temp2;
$ret = preg_match( '@<creditcard[^>]+>(.*?)</creditcard>@i', $hotel, $credit );
$temp[ 'creditcard' ] = explode( ',', $credit[ 1 ] );
$ret = preg_match( '@<lastupdate day="([0-9]+)" month="([0-9]+)" year="([0-9]+)"/>@i', $hotel, $updates );
$temp[ 'lastupdate' ] = sprintf( '%s-%s-%s', $updates[ 3 ], $updates[ 2 ], $updates[ 1 ] );
$this->data[] = $temp;
unset( $temp );
}
}
}
?>