価格.com API + Relaxer
http://apiblog.kakaku.com/KakakuItemSearchV1.0.html
価格.comでもAPIが公開されました。
以前YahooJapanAPIの時は、JaxMeなどでマッピングしたけど、
今度はrelaxerを使用してみようということで実験
Relaxer1.1はインストール済みで、
RELAX_HOMEが環境変数に登録してあり、%REALX_HOME%\binにPATHが通っているものとする。
商品検索API
を見てみると、
サンプルがあるので、それを元に、rngを生成してみます。
#kakakuResult.xml
<?xml version="1.0" encoding="utf-8" ?> <ProductInfo> <NumOfResult>794</NumOfResult> <Item> <ProductID>00200614970</ProductID> <ProductName>VAIO VGN-TX72B/B</ProductName> <MakerName>SONY</MakerName> <CategoryName>パソコン本体>Windowsノート</CategoryName> <PvRanking>8</PvRanking> <ImageUrl>http://img.kakaku.com/images/productimage/m/00200614970.jpg</ImageUrl> <ItemPageUrl>http://kakaku.com/item/00200614970/</ItemPageUrl> <BbsPageUrl>http://kakaku.com/bbs/Main.asp?PrdKey=00200614970</BbsPageUrl> <ReviewPageUrl>http://kakaku.com/prdevaluate/evaluate.asp?PrdKey=00200614970</ReviewPageUrl> <LowestPrice>169050</LowestPrice> <NumOfBbs>122</NumOfBbs> </Item> <Item> <ProductID>00200614970</ProductID> <ProductName>VAIO VGN-TX72B/B</ProductName> <MakerName>SONY</MakerName> <ItemPageUrl>http://kakaku.com/item/00200614970/</ItemPageUrl> <BbsPageUrl>http://kakaku.com/bbs/Main.asp?PrdKey=00200614970</BbsPageUrl> <ReviewPageUrl>http://kakaku.com/prdevaluate/evaluate.asp?PrdKey=00200614970</ReviewPageUrl> <LowestPrice>169050</LowestPrice> </Item> </ProductInfo>
- ここでは、ResultSetのmediumとminiのItemを混合して含めています。それぞれで別の解析をするのであれば、別々でもかまいません。
上記で作成したxmlのあるディレクトリで以下のように実行します。
>java -jar %relaxer_home%\lib\trang.jar kakakuResult.xml kakaku.result.rng
ここではtrangが同梱されていたので、trangを利用して、rngに変換しています。
コマンドを実行した結果、以下のようなファイルが出来ていると思います。
#kakaku.result.rng
<?xml version="1.0" encoding="UTF-8"?> <grammar ns="" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> <start> <element name="ProductInfo"> <element name="NumOfResult"> <data type="integer"/> </element> <oneOrMore> <element name="Item"> <element name="ProductID"> <data type="integer"/> </element> <element name="ProductName"> <text/> </element> <element name="MakerName"> <text/> </element> <optional> <element name="CategoryName"> <text/> </element> <element name="PvRanking"> <data type="integer"/> </element> <element name="ImageUrl"> <data type="anyURI"/> </element> </optional> <element name="ItemPageUrl"> <data type="anyURI"/> </element> <element name="BbsPageUrl"> <data type="anyURI"/> </element> <element name="ReviewPageUrl"> <data type="anyURI"/> </element> <element name="LowestPrice"> <data type="integer"/> </element> <optional> <element name="NumOfBbs"> <data type="integer"/> </element> </optional> </element> </oneOrMore> </element> </start> </grammar>
注意点
- 2006/10/10現在、HP上では、NumOfBbsがminiとmedium共通となっていますが、実際はmediumのみのようです。
さて、CategoryName、PvRanking、ImageUrlがまとめて、optionalと指定されていますが、このまま、relaxerを使用して出力すると、うまくいきませんでしたので、ここで手を加えます。
#修正後
<?xml version="1.0" encoding="UTF-8"?> <grammar ns="" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> <start> <element name="ProductInfo"> <element name="NumOfResult"> <data type="integer"/> </element> <oneOrMore> <element name="Item"> <element name="ProductID"> <data type="integer"/> </element> <element name="ProductName"> <text/> </element> <element name="MakerName"> <text/> </element> <optional> <element name="CategoryName"> <text/> </element> </optional> <optional> <element name="PvRanking"> <data type="integer"/> </element> </optional> <optional> <element name="ImageUrl"> <data type="anyURI"/> </element> </optional> <element name="ItemPageUrl"> <data type="anyURI"/> </element> <element name="BbsPageUrl"> <data type="anyURI"/> </element> <element name="ReviewPageUrl"> <data type="anyURI"/> </element> <element name="LowestPrice"> <data type="integer"/> </element> <optional> <element name="NumOfBbs"> <data type="integer"/> </element> </optional> </element> </oneOrMore> </element> </start> </grammar>
CategoryName、PvRanking、ImageUrlを別々にoptionalとしました。
再びコマンドプロンプトで以下のように実行します。
>relaxer -java.pattern.visitor -java.package:hoge.mokkouyou.kakaku.xml.result -dir:src -dir.package kakaku.result.rng
ここでは、それぞれの引数についての説明は省略します。
Visitorパターンはとりあえず指定しましたが、なくてもいいと思います。
└─src └─hoge └─mokkouyou └─kakaku └─xml └─result IRNode.java IRVisitable.java IRVisitor.java RStack.java RVisitorBase.java StartProductInfo.java StartProductInfoItem.java UJAXP.java URelaxer.java URVisitor.java
上記のようなファイルが生成されました。
前回はわざわざRequestを発行するクラスも作っていましたが、
今回は別に必要ないようですので、ここで実験します。(前回も必要なかった?)
StartProductInfo root= new StartProductInfo(new URL( “http://api.kakaku.com/Ver1/ItemSearch.asp?Keyword=%83o%83C%83I& CategoryGroup=pc&ResultSet=medium&SortOrder=pricerank&PageNum=1”));
- URL部分は実際には一行にしてください。-
- Proxyの設定が必要な人は、
System.setProperty(“http.proxyHost”,”hostName”);
System.setProperty(“http.proxyPort”,”hostPort”);
で設定してください。
これで、rootはすでに、ドキュメント構造をもったオブジェクトとなっています。
必要に応じて、VOを作って、データのコピーをすればいいでしょう。
あとは、実際にURLを組み立てるUtilityなどがあると便利なので作っておきましょう。
#RequestParameterBuilder.java
package hoge.mokkouyou.kakaku.util; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class RequestParameterBuilder { /* SORT */ private static final String SORT_POPULARITYRANK = "popularityrank"; private static final String SORT_DATERANK = "daterank"; private static final String SORT_PRICERANK_DESC = "-pricerank"; private static final String SORT_PRICERANK_ASC = "pricerank"; /*Category*/ private static final String CATEGORY_ALL = "ALL"; private static final String CATEGORY_BRAND = "Brand"; private static final String CATEGORY_SPORTS = "Sports"; private static final String CATEGORY_GAME = "Game"; private static final String CATEGORY_CAMERA = "Camera"; private static final String CATEGORY_KADEN = "Kaden"; private static final String CATEGORY_PC = "Pc"; /* ResultSetValues */ private static final String RESULT_SET_MEDIUM = "medium"; private static final String RESULT_SET_MINI = "mini"; /* keys */ private static final String SORT_ORDER_KEY = "SortOrder"; private static final String PAGE_NUM_KEY = "PageNum"; private static final String CATEGORY_GROUP_KEY = "CategoryGroup"; private static final String RESULT_SET_KEY = "ResultSet"; private static final String KEYWORD_KEY = "Keyword"; private static final String BASE_URL = "http://api.kakaku.com/Ver1/ItemSearch.asp?"; private Map params = new HashMap(); private int pageNum = 1; private String proxyHost; private String proxyPort; public RequestParameterBuilder() { init(); } private void init() { setResultSetTypeMedium(); setCategoryGroupTypeALL(); setSortOrderKeywordRank(); setPageNum(1); } public void setKeyWord(String keyword) { params.put(KEYWORD_KEY, keyword); } public void setResultSetTypeMini() { params.put(RESULT_SET_KEY, RESULT_SET_MINI); } public void setResultSetTypeMedium() { params.put(RESULT_SET_KEY, RESULT_SET_MEDIUM); } public void setCategoryGroupTypePC() { params.put(CATEGORY_GROUP_KEY, CATEGORY_PC); } public void setCategoryGroupTypeKaden() { params.put(CATEGORY_GROUP_KEY, CATEGORY_KADEN); } public void setCategoryGroupTypeCamera() { params.put(CATEGORY_GROUP_KEY, CATEGORY_CAMERA); } public void setCategoryGroupTypeGame() { params.put(CATEGORY_GROUP_KEY, CATEGORY_GAME); } public void setCategoryGroupTypeSports() { params.put(CATEGORY_GROUP_KEY, CATEGORY_SPORTS); } public void setCategoryGroupTypeBrand() { params.put(CATEGORY_GROUP_KEY, CATEGORY_BRAND); } public void setCategoryGroupTypeALL() { params.put(CATEGORY_GROUP_KEY, CATEGORY_ALL); } public void setSortOrderPriceAsc() { params.put(SORT_ORDER_KEY, SORT_PRICERANK_ASC); } public void setSortOrderPriceDesc() { params.put(SORT_ORDER_KEY, SORT_PRICERANK_DESC); } public void setSortOrderDateRank() { params.put(SORT_ORDER_KEY, SORT_DATERANK); } public void setSortOrderPopularityRank() { params.put(SORT_ORDER_KEY, SORT_POPULARITYRANK); } public void setSortOrderKeywordRank() { if(params.containsKey(SORT_ORDER_KEY)) { params.remove(SORT_ORDER_KEY); } } /** * ページ番号を指定する。<br> * ページは5件毎に表示されるので、そこは計算する。 * @param num ページ番号 */ public void setPageNum(int num) { pageNum = num; setPageNum(); } private void setPageNum() { params.put(PAGE_NUM_KEY, String.valueOf(pageNum)); } public void nextPage() { pageNum++; setPageNum(); } public void prevPage() { if(pageNum > 1) { pageNum--; } setPageNum(); } public URL toURL() { URL url = null; try { if(proxyHost != null) { System.setProperty("http.proxyHost", proxyHost); } if(proxyPort != null) { System.setProperty("http.proxyPort", proxyPort); } StringBuilder sb = new StringBuilder(BASE_URL); for (Iterator iter = params.keySet().iterator(); iter.hasNext();) { String key = (String) iter.next(); sb.append(key) .append("=") .append(URLEncoder.encode((String) params.get(key), "SJIS")) .append("&"); } String substring = sb.substring(0, sb.length() - 1); url = new URL(substring); } catch (MalformedURLException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return url; } public String getProxyHost() { return proxyHost; } public void setProxyHost(String proxyHost) { this.proxyHost = proxyHost; } public String getProxyPort() { return proxyPort; } public void setProxyPort(String proxyPort) { this.proxyPort = proxyPort; } }
簡単ですが、こんな感じで十分ではないでしょうか。
デフォルトだと、ALLカテゴリで、keywordランキング順に、mediumで、1ページ目を取得します。
以下のように使用します。
RequestParameterBuilder builder = new RequestParameterBuilder(); builder.setKeyWord("バイオ"); builder.setCategoryGroupTypePC(); builder.setResultSetTypeMini(); builder.setProxyHost("host"); builder.setProxyPort("port"); URL url = builder.toURL(); StartProductInfo root = new StartProductInfo(url);
あとは、どういうロジックを組むかによって、
IRVisitor継承クラスを用意して、StartProductinfoとともに、URVisitorを使って、Visitorパターンでオブジェクト組み立てたり、表示がんばったりしてみたり・・・
といったところでしょうか。
なんだかとっても簡単ですね・・・
YahooJapan用APIはなんだったんだろう・・・