Yahoo! JAPAN デベロッパーネットワーク

http://developer.yahoo.co.jp/
利用するには Yahoo! JAPAN ID でログインしたのち,アプリケーションID を登録する必要がある.
登録の際の必須項目は以下のとおり:

  • Yahoo! JAPAN ID
  • 連絡先メールアドレス(Yahoo! JAPAN ID の登録元(例:プロバイダ)か Yahoo! JAPAN メールアドレス)
  • OAuth 利用 (-> とりあえず「利用しない」を選択)
  • ガイドラインへの同意

得られるアプリケーション ID は乱数っぽい文字列であり,長い(^^)

次のように GET リクエストを投げればよい.query は URLエンコードすればよいみたい.

http://search.yahooapis.jp/WebSearchService/V1/webSearch?appid=<あなたのアプリケーションID>&query=%e6%b2%96%e7%b8%84&results=2

試しに作ってみた Java プログラムを以下に示す.
なお,Xerces のバージョンは 1.4.4 である.
(XPath を使えば,もっとスマートに作れるのだが・・)

// Yahoo! JAPAN デベロッパーネットネットワークにおいて提供される「ウェブ検索」の利用
//
// [参照]
// http://developer.yahoo.co.jp/webapi/search/websearch/v1/websearch.html
//
// [改訂履歴]
// 14-Sep-2009 : とりあえず動くバージョンを作った
//
// [コンパイルと実行]
// javac -cp /usr/local/xerces/xerces.jar YahooWebSearch.java
// java -cp /usr/local/xerces/*:. YahooWebSearch

import org.apache.xerces.parsers.DOMParser;	//DOM パーサーのパッケージ
import org.w3c.dom.Document;	// Documentオブジェクトのパッケージ
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
//import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Attr;
import org.apache.xerces.dom.DeepNodeListImpl;
import java.io.*;
import java.net.*;
import java.util.*;

public class YahooWebSearch {

    // 検索結果を格納するためのリスト (generic programming に対処)
    java.util.LinkedList<YahooSearchResult> list;

    public YahooWebSearch(){
	// 検索結果を格納するためのリストを生成
	list = new java.util.LinkedList<YahooSearchResult>();
    }

    // Yahoo! JAPAN Developer Network が定める引数のデフォルト値にしたがって検索する
    public int search(String yahooAppID, String query){
	int ret;
	ret = search(yahooAppID, query, 1, 10);
	return ret;
    }

    // seach() メソッドにおける引数は,Yahoo! JAPAN デベロッパーネットワークの制限により
    // 下記のとおり:
    //   ・start の下限は 1
    //   ・results の上限は 50
    // 返し値は「得られた検索結果の数」
    //
    // (注意)
    // clearList() を呼ばずに search()メソッドが続けて呼ばれた場合,
    // list の中身は次々と追加される.
    public int search(String yahooAppID, String query, int start, int results){
	int numberOfSearchResult;

	// 引数に関するチェック
	// (Yahoo! に問い合わせる前に,アプリケーション側ではじく)
	if(start < 1 || results < 1 || results > 50 || start + results - 1 > 1000)
	    return -1;

	// query を URLエンコードする
	String encodedQuery = "";
	try {
	    encodedQuery = URLEncoder.encode(query, "UTF-8");
	} catch(UnsupportedEncodingException e){
	    System.err.println("UnsupportedEncodingException : " + e);
	    return -1;
	}

	// GETリクエストの URL を生成
	String requestURL;
	requestURL = "http://search.yahooapis.jp/WebSearchService/V1/webSearch";
	requestURL = requestURL + "?appid=" + yahooAppID;
	requestURL = requestURL + "&query=" + encodedQuery;
	requestURL = requestURL + "&start=" + start;
	requestURL = requestURL + "&results=" + results;  // 返してもらう件数


	try{
	    // DOMパーサーインスタンスを生成
	    DOMParser parser = new DOMParser();

	    //System.out.println(requestURL);	//デバッグ用

	    // 引数でXMLファイルまたは URL を指定し、Parseメソッドで
	    // 妥当性を検証
	    parser.parse(requestURL);
		
	    // getDocumentメソッドでドキュメントオブジェクトを生成
	    Document xdoc = (Document)parser.getDocument();
	    
	    // ルート・エレメント(ルートノード)の取得
	    Element root = xdoc.getDocumentElement();
	    
	    /* ----------------------------------------- */
	    // Entry を取得し,リストへ登録
	    NodeList nl;
	    nl = root.getElementsByTagName("Result");
	    
	    // デバッグ用
	    /*
	      if(nl != null){
	      System.out.println("entry=" + nl.getLength());
	      }
	    */
	    
	    numberOfSearchResult = nl.getLength();

	    for(int i = 0; i < nl.getLength(); i++){
		Node n = nl.item(i);
		    
		//System.out.println(n.getNodeName());	// デバッグ用
		/*
		Element e = (Element)n;
		System.out.println(i+1 + " : " + e.getFirstChild().getNodeValue());
		*/

		YahooSearchResult b = new YahooSearchResult(n);
		list.add(b);
	    }
		
	    parser.reset();	// 要るのか否か不明だが,一応,書いておく
	    
	} catch(Exception e){
	    System.err.println(e + ":例外発生");
	    return -1;
	}

	return numberOfSearchResult;
    }

    // 検索結果の個数,すなわちリストの長さを返す
    public int getSearchResultSize() { return list.size(); }

    // list に格納された検索結果を削除したい場合に呼ぶ
    public void clearSearchResult(){ list.clear(); }

    // i 番目の title を返す
    public String getIndexedTitle(int i) { return list.get(i).getTitle(); }

    // i 番目のリンク(URL)を返す
    public String getIndexedUrl(int i) { return list.get(i).getUrl(); }

    // i 番目の summary を返す
    public String getIndexedSummary(int i) { return list.get(i).getSummary(); }

    // i 番目の modificatioDate を返す
    public Calendar getIndexedModificationDate(int i) {
	return list.get(i).getModificationDate();
    }

    // i 番目の MIME type を返す
    public String getIndexedMimeType(int i) {
	return list.get(i).getMimeType();
    }

    // 全ての検索結果(リスト内の全要素)を出力
    // (注)デバッグ用
    public void printAllSearchResult(){
	for(int i = 0; i < list.size(); i++){
	    System.out.println("--" + (i+1) + "--");
	    System.out.println(list.get(i));
	}
    }

    ////////// 内部クラス ///////////

    public class YahooSearchResult {
	String title = null;	// 検索されたWebページのタイトル
	String summary = null;		// サマリ
	String url = null;	// 検索された Webページの URL
	String clickURL = null;
	Calendar modificationDate;	// 最終更新日(月は 0..11 の値をとる)
	String mimeType = null;

	public YahooSearchResult(Node result){
	    NodeList childOfResult = result.getChildNodes();
	    for(int j = 0; j < childOfResult.getLength(); j++){
		Node n = childOfResult.item(j);
		if(n.getNodeType() != Node.ELEMENT_NODE) continue;
		
		Element e = (Element)n;	// getAttribute()を使うためのキャスト
		
		if(e.getNodeName().equals("Title") == true
		   && e.hasChildNodes() == true){
		    title = new String(e.getFirstChild().getNodeValue());
		    //System.out.println(title);
		} else if(e.getNodeName().equals("Summary") == true
			  && e.hasChildNodes() == true){
		    summary = new String(e.getFirstChild().getNodeValue());
		    //System.out.println(summary);
		} else if(e.getNodeName().equals("Url") == true
			  && e.hasChildNodes() == true){
		    url = new String(e.getFirstChild().getNodeValue());
		    //System.out.println(url);
		} else if(e.getNodeName().equals("ClickUrl") == true
			  && e.hasChildNodes() == true){
		    clickURL = new String(e.getFirstChild().getNodeValue());
		    //System.out.println(clickURL);
		} else if(e.getNodeName().equals("ModificationDate") == true
			  && e.hasChildNodes() == true){
		    String modDate = new String(e.getFirstChild().getNodeValue());
		    //System.out.println(modDate);	//デバッグ用

		    modificationDate = new GregorianCalendar();
		    
		    // modificationTime は秒単位で与えられるため,1000倍して
		    // setTimeInMillis() に渡す.
		    // ただし,Yahoo! は「年月日」までをデータとして保持するため,
		    // 「時・分・秒」は全てゼロがセットされる.
		    // また,「月」は Calendar クラスの仕様に基づき,0..11 の値を
		    // とることに注意.
		    modificationDate.setTimeInMillis(Long.parseLong(modDate) * 1000);

		    // デバッグ用
		    /*
		    int year = modificationDate.get(Calendar.YEAR);
		    int month = modificationDate.get(Calendar.MONTH);
		    int date = modificationDate.get(Calendar.DATE);
		    int hour = modificationDate.get(Calendar.HOUR_OF_DAY);
		    int minute = modificationDate.get(Calendar.MINUTE);
		    int second = modificationDate.get(Calendar.SECOND);
		    System.out.println(year + "年" + month + "月" + date + "日");
		    System.out.println(hour + "時" + minute + "分" + second + "秒");
		    */
		} else if(e.getNodeName().equals("MimeType") == true
			  && e.hasChildNodes() == true){
		    mimeType = new String(e.getFirstChild().getNodeValue());
		    //System.out.println(mimeType);
		} else if(e.getNodeName().equals("Cache") == true
			  && e.hasChildNodes() == true){
		    ;	// とりあえず NOP
		}
	    }
	}

	public String getTitle(){ return title; }
	public String getUrl(){ return url; }
	public String getSummary(){ return summary; }
	public Calendar getModificationDate() { return modificationDate;}
	public String getMimeType(){ return mimeType; }

	public String toString(){
	    String s = "";
	    s = s + getTitle() + "\n";
	    s = s + getSummary() + "\n";
	    s = s + getUrl() + "\n";

	    Calendar cal = getModificationDate();
	    s = s + cal.get(Calendar.YEAR) + "年"
		+ (cal.get(Calendar.MONTH)+1) + "月"
		+ cal.get(Calendar.DATE) + "日\n";
	    
	    s = s + getMimeType();

	    return s;
	}
    }

    public static void main(String[] args){

	// Yahoo! JAPAN アプリケーションID
	String appID = "Yahoo! JAPAN Application ID";
	String query = "パターン";

	// Yahoo! JAPAN が検索結果を1件しか返さない例文 (^^)
	//String query = "パターン認識ってご飯と一緒に食べるものでしょうでも夕飯はパスタだよねってそれあなたの好物であって私は欲しくない";

	YahooWebSearch ws = new YahooWebSearch();
	int ret;

	// 検索上位の 1〜50件目を得る
	ret = ws.search(appID, query, 1, 50);
	if(ret >= 50){
	    // 検索上位の 51〜100件目を得る
	    ret = ws.search(appID, query, 51, 50);
	}

	// 入手した検索結果のクリア
	//ws.clearSearchResult();


	// 第4引数が parameter error(上限 50 を越えている)
	/*
	ret = ws.search(appID, query, 51, 51);
	if(ret == false){
	    System.err.println("invalid parameters");
	}
	*/

	System.out.println(ws.getSearchResultSize());

	// デバッグ用のメソッド呼び出し(全ての検索結果を出力)
	// ws.printAllSearchResult();

	// 全ての検索結果をひとつずつ取り出す
	for(int i = 0; i < ws.getSearchResultSize(); i++){
	    System.out.println("== result : " + (i+1) + " ==");
	    System.out.println(ws.getIndexedTitle(i));
	    System.out.println(ws.getIndexedSummary(i));
	    System.out.println(ws.getIndexedUrl(i));

	    Calendar cal = ws.getIndexedModificationDate(i);
	    System.out.println(cal.get(Calendar.YEAR) + "年"
			       + (cal.get(Calendar.MONTH)+1) + "月"
			       + cal.get(Calendar.DATE) + "日");

	    System.out.println(ws.getIndexedMimeType(i));
	}
    }
}