初出 JAVA PRESS Vol.35 (2004.03)

Google Web API をコールするクライアントを作る

はじめに

Webサービスを用いて検索を行う、そんなことができるのでしょうか。それができるのです。検索の巨人Googleは検索サービスをWebサービスとして公開しており、誰もがこれを自由に使用することができるのです。これでを使うことでWebサービスと検索の両方を一緒に試すことができます。

さて、Webサービスを使用するには道具があるほうが、なにかと便利です。今回、使用する道具はJAX-RPC (Java API for XML-based RPC))です。JAX-RPCはJava Comunity Processで策定されており、Webサービスで使用されるSOAPをJavaで使用することができます。

JAX-RPCのリファレンスインプリメンテーションはSun Microsystemsが無償で公開しているJava Web Services Developer Pack (Java WSDP)やJ2EE 1.4 に含まれています。今回はJava WSDPを使用してGoogleのWebサービスを利用するクライアントアプリケーションを作成します。

Google Web API

GoogleのWebサービスはGoogle Web API と呼ばれており、次の 3 種類のサービスが提供されています。

どれも Google の Web サイトで提供されているものと同等のものが利用できます。ただし、Google Web APIを使用できるのは1日に1,000回までという制約があります。

Google Web APIの登録

Google Web API を使用するには、まず Developer's Kit をダウンロードしましょう。ダウンロードはGoogle Web APIのページhttp://www.google.com/apis/から行うことができます。

Developer's Kit には Java のライブラリとサンプルコード、.NET (C# と Visual Basic) のサンプルコードなどが含まれています。

Developer's Kitをダウンロードしたら、次にライセンスキーを取得します。Google Web APIを使用するには誰でも使用することができますが、使用するGoogleへの登録が必要です。登録するとライセンスキーを発行するので、このキーを使用してWeb APIを利用します。

ライセンスキーの発行にはGoogle Accountが必要です。Web APIのページのCreate Accountsのリンクから Accountの登録ができます。登録は無料です。

メールアドレスとパスワード、またプライバシーポリシーのチェックを行って"Create My Google Account"をクリックすればOKです。すると、自動的にGoogle Accountの登録確認メールが届くはずです。このメールに書いてあるURLをブラウザで開くとメールでの確認は終了です。

次にそのページの"Click Here to Continue."でライセンスキーが発行されます。発行されたライセンスキーはメールで届くはずです。ライセンスキーはWeb APIを使用するときには必ず必要になります。でも、もしライセンスキーを忘れてしまったとしても、Google Accountsの Webページで再送付してもらえます。

さあ、これで準備はできました。

Google Web API
図 1 Google Web API

 

Google Web APIs Developer's Kit

せっかく、Developer's Kitをダウンロードしたのですから、まずはじめにこれでGoogle Web APIを使用してみましょう。

まず、ダウンロードしたgoogleapi.zipを解凍します。解凍した結果のディレクトリ構造は図2のようになっており、JavaのサンプルクラスGoogleAPIDemoも提供されています。

googleapi/
        dotnet/                       .NET のサンプル (C# と Visual Basic)
        javadoc/                      googleapi.jar の JavaDoc
        licenses/                     googleapi.jar で使用しているライブラリのライセンス
        soap-samples/                 Google Web API の SOAP メッセージとレスポンスのサンプル
        APIs_Reference.html           Google Web API のリファレンスマニュアル
        googleapi.jar                 Java のライブラリ
        GoogleAPIDemo.java            Java のサンプル
        GoogleSearch.wsdl             Google Web API の WSDL
        LICENSE.txt                   Google Web API のライセンス
        README.txt                    README
図 2 googleapi.zip の構成

さっそく、このサンプルを試してみましょう。googleapi.jarの中には GoogleAPIDemo.classが含まれているので、あらためて GoogleAPIDemo.javaをコンパイルする必要はありません。

GoogleAPIDemoの使い方を図3に示しました。もしHTTPプロキシーを使用されているときは、java のオプションで-Dhttp.proxyHost=ホスト名 -Dhttp.proxyPort=ポート番号と指定してください。

Windows XPで実行した結果を図4に示します。XXXXXX の部分はライセンスキーです。皆さんが試されるときにはこの部分に自分のキーを入れてください。ここではjava tiger site:www.gihyo.co.jpというキーワードで検索を行い、8件の結果がヒットしました。

検索の方法は基本的には Web サイトでの検索方法と同じです。詳しくは Developer's KitのAPIs_Reference.htmlをご覧ください。

GoogleAPIDemoクラスの検索の部分だけ取り出してみたのがリスト1です。GoogleSearchオブジェクトを生成して、必要なプロパティをセットし、検索を行うdoSearchメソッドをコールしています。単にこれだけのプログラムで Google Web APIが実行できます。

JAX-RPCを使ってもGoogleAPIDemoクラスと同じように、簡単にWebサービスのクライアントを作ることができます。次章ではJAX-RPCを使ってGoogle Web APIのクライアントを作ってみましょう。

java -cp googleapi.jar com.google.soap.search.GoogleAPIDemo <ライセンスキー> <処理> <処理の引数>
        処理       search, cached, spell
        処理の引数  search   検索する文字列  複数の文字列の場合は "" で囲む
                   caced    URL
                   spell    スペルチェックする文字列  複数の文字列の場合は "" で囲む
図 3 GoogleAPIDemo の使用法

 

GoogleAPIDemo の実行結果
図 4 GoogleAPIDemo の実行結果

 

    String clientKey = args[0];
    String directive = args[1];
    String directiveArg = args[2];
 
    GoogleSearch s = new GoogleSearch();               // GoogleSearch オブジェクトの生成
    s.setKey(clientKey);                               // ライセンスキーをセット
 
    try {
        if (directive.equalsIgnoreCase("search")) {
s.setQueryString(directiveArg); // 検索実行 GoogleSearchResult r = s.doSearch(); System.out.println("Google Search Results:"); System.out.println("======================"); System.out.println(r.toString()); // 結果の表示 } } catch (GoogleSearchFault f) { System.out.println("The call to the Google Web APIs failed:"); System.out.println(f.toString()); }
リスト 1 GoogleAPIDemoクラスの検索処理部分

 

JAX-RPCを用いてGoogle Web APIを使用する

JAX-RPCはWebサービスのサーバとクライアントの両方に対応しているのですが、今回はクライアントだけの利用になります。本稿ではJava WSDP 1.3を使用し、Windows XPで動作させました。

Java WSDPにはJAX-RPC以外にも次に示すAPIが提供されています。

その他にTomcatとAntが含まれています。

Java WSDPのインストール

Java WSDPは http://java.sun.com/webservices/downloads/webservicespack.html からダウンロードできます。Windows版だけでなく、LinuxとSolaris版があります。

Windows版のJava WSDPのファイル名はjwsdp-1_3-windows-i586.exeで、サイズは約26MBあります。インストールを行う前に、古いバージョンのJava WSDPがインストールされていたらアンインストールしておきます。 またインストールには1.4以上のバージョンのJ2SDKが必要です。

jwsdp-1_3-windows-i586.exeを実行するとインストーラが起動するので、指示に従って進めていけばインストールが完了します。後は必要に応じてPATH環境変数を設定してください。

SOAPとWSDL

多くのWebサービスはプロトコルにSOAPを使用しています。SOAPはXMLをベースにしており、サーバに送信するXMLドキュメントの中に利用するサービスやサービスへの入力を記述します。サーバからの返答もXMLで記述されています。

SOAPが送受信するXMLドキュメントはSOAPエンベロップと呼ばれ、エンベロップの中にサービスを利用するための情報を記述します。

Webサービスを利用するには、提供しているサービスに関する詳細な定義がなければなりません。この用途に使用されるのが、WSDL (Web Service Description Language) です。通常はWSDLを使用してSOAPのサービスを定義します。

詳細は省略しますが、WSDLでは表1に示されるような情報を定義しています。

Google Web APIのDeveloper's KitにもWSDLファイルが含まれています。また、http://api.google.com/GoogleSearch.wsdlでも見ることができます。Google Web APIのWSDLには表2に示す3つのオペレーションと、表3に示す3つの型が定義されていることが分かります。

WSDLの定義に従ってXMLを組みたて、それをSOAPエンベロップに入れてサーバに送信することでWebサービスを利用することができるのです。

表 1 WSDL の構成
要素 説明

サービスが使用するデータ型を XML Schema を使用して定義する

メッセージ オペレーション (メソッド) の入力、出力を定義する
ポートタイプ 定義されたメッセージを使用して、オペレーションのシグネチャを記述する
バインディング オペレーションの実装の情報を記述する。例えば、SOAP の RPC を使用するなど
サービス サービスを提供するエンドポイントの URL などを定義する

 

表 2 WSDL で定義されているオペレーション
オペレーション

GoogleSearchResult doGoogleSearch(String key, String q, int start, int maxResults, boolean filter, String restric, boolean safeSearch, String lr, String ie, String oe)

Web の検索を行います。

入力

key Google Web APIのライセンスキー。
q 検索に使用するキーワード。site: link: などの特別構文も含むことができます。
start 複数の検索結果がある場合に、startで指定された件数からの結果を返します。
maxResults 一度に返される検索結果の最大値を指定します。10以上は10とみなされます。
filter WebからGoogleを使用すると、「」と表示されることがあります。filterはそれと同じ動作を指定することができます。trueの場合は同一のページや同じホストから3件以上のヒットがあった場合、重複したヒットを結果から削除します。
restrict 日本のページだけに検索を制限することや、特定トピック検索などを行うときに指定します。特定トピックにはアメリカ政府(unclesam)、Linux (linux)、Macintosh (mac)、FreeBSD (bsd)の4種類があります(カッコ内の文字列をrestricに使用します)。日本でLinuxに関することなどのように制限のANDをとるときには "." (ドット) を使用し、countryJP.linuxのように表記します。なにも指定しない場合は "" (空文字列) を使用します。
safeSearch 性的なコンテンツなどを検索結果から削除するかどうかを指定します。
lr Language Restrictの略で、特定の言語に限定して検索を行います。複数の言語から検索を行う場合は、lang_en|lang_jpというように"|"で言語をつなげて記述します。また、日本語以外という場合にはマイナスをつけて-lang_jpのように記述します。
ie 入力に使用する文字コードを指定します。しかし、実際に使用できるのはUTF-8のみです。
oe 出力に使用する文字コードを指定します。しかし、これもieと同様UTF-8のみ使用できます。

出力

検索結果。

byte[] doGetCachedPage(String key, String url)

Web ページのキャッシュの取得します。

入力

key Google Web APIのライセンスキー。
url

キャッシュを取得するWebページのURL

出力

Webページのキャッシュをbase64を使用してエンコードしたもの。

String doSpellingSuggestion(String key, String phrase)

スペルチェックを行います。

入力

key Google Web APIのライセンスキー。
phrase

スペルチェックを行うフレーズ

出力

スペルチェックの結果。

 

表 3 WSDL で定義されている型
型名 説明
GoogleSearchResult 検索の戻り値を保持するクラス。検索結果のカウント、時間などを持つ。検索されたWebページの情報はResultElementクラスの配列として持っている。
ResultElement 検索されたWebページの情報を保持するクラス
DirectoryCategory 検索されたWebページのカテゴリを保持するクラス

 

JAX-RPCを使用したSOAPクライアント

JAX-RPCでSOAPクライアントを作成するには次の 3 種類の方法があります。

DIIはいうなればリフレクションのSOAP版のようなものです。DIIを使用すれば、アプリケーションの実装時にはSOAPサービスの名前や引数などが不明でも、実行時に動的にこれらを設定して使用することができます。DIIではWSDLファイルを使用することもできますが、使用しなくてもクライアントを作成することができます。

スタブはWSDLファイルを使用して生成されるクラスです。スタブを使用すると、RMIなどと同じように、リモートのSOAPサービスをあたかも通常のメソッドのようにコールすることができます。スタブはWebサービスを利用する前に、ツールを使用して生成しておく必要があります。

Dynamic Proxyはjava.lang.reflect.Proxyクラスを利用した、WSDLファイルから動的にプロキシを生成する方法です。

今回は最も使用頻度が高いと思われるスタブを使用してクライアントを作成してみます。

WSDL を使用したスタブの生成

WSDLからスタブを生成するにはJAX-RPCに付属しているツールwscompileを使用します。wscompileはJava WSDPをインストールしたディレクトリを<JWSDP_HOME>とすると、<JWSDP_HOME>\jaxrpc\binに配置されています。

wscompileを使ってスタブを作成するにはWSDLファイルの位置などを記述した設定ファイルが必要です。設定ファイルはconfigurationタグがルートになり、その下にwsdlタグを使用してWSDLファイルの情報を記述します。

wsdlタグには属性としてlocationとpackageNameが使用できます。locationでWSDLの位置を示します。URLを使用できるのでネットワークを介してWSDLファイルを参照することも可能です。

packageNameは作成するスタブのパッケージを指定するものです。

今回使用した設定ファイルconfig.xmlをリスト2に示します。設定ファイルが用意できたらスタブを作成します。

  wscompile -gen config.xml

wscompileを実行するとクラスファイルが生成されます。生成されたJavaのソースファイルを残すためにはオプション-keepを使用します。その他のオプションに関してはJAX-RPCのドキュメントを参照してください。

GoogleSearch.wsdlからスタブを作成するとなんと32個のクラスが生成されました。主なクラスを表4に示します。WSDLの名前にServiceがついたものがスタブを生成するファクトリ、Portとついたものがスタブです。

残りの3つのクラス(GoogleSearchResult、ResultElement、DirectoryCategoryクラス)はWSDLファイルに定義されている型をクラスにしたものです。これらのクラスはサービスが提供するメソッドの引数や戻り値に使われます。

 

<?xml version="1.0" encoding="UTF-8"?>
<configuration 
       xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
  <wsdl location="GoogleSearch.wsdl"
       packageName="google.webapi"/>
</configuration>
リスト 2 wscompileの設定ファイル config.xml

 

表 4 WSDLから生成される主なクラス
型名 説明
GoogleSearchService

スタブのインスタンスを生成するためのファクトリのインタフェース

GoogleSearchService_Impl スタブのインスタンスを生成するためのファクトリの実装クラス
GoogleSearchPort スタブのインタフェース
GoogleSearchPort_Stub スタブの実装クラス
GoogleSearchResult

検索の戻り値

ResultElement 検索結果を保持するクラス
DirectoryCategory 検索結果のカテゴリを保持するクラス

 

WSDLファイルの型の定義は、Cの構造体のようにプロパティの名前とその型で定義されます。たとえばDirectoryCategoryはGoogleSearch.wsdlの中でリスト3のように定義されています。

詳しくは説明しませんが、complexTypeというのが構造体のような複合型を表わしています。その要素がelementタグで定義されます。

1つめのelementタグではfullViewableNameという名前のプロパティが定義されており、その型は文字列であることを示しています。同様にspecialEncodingも文字列であることが分かります。

ここで使用されているxsd:stringという型はXMLSchemaで定義されているものです。XMLSchemaでは基本的な型を定義しており、WSDLファイルではこれを使用して型の定義を行います。

この定義から生成されたクラスがDirectoryCategoryクラスです(リスト4)。プロパティとして文字列のfullViewableNameとspecialEncodingがあり、それぞれgetterメソッドとsetterメソッドが定義されています。

残りのクラスはSOAPのメッセージからJavaのオブジェクトを生成したり、JavaからSOAPのメッセージを作成するのに使用されています。

      <xsd:complexType name="DirectoryCategory">
        <xsd:all>
          <xsd:element name="fullViewableName" type="xsd:string"/>
          <xsd:element name="specialEncoding" type="xsd:string"/>
        </xsd:all>
      </xsd:complexType>
リスト 3 DirectoryCategoryの定義

 

public class DirectoryCategory {
    protected java.lang.String fullViewableName;
    protected java.lang.String specialEncoding;
    
    public DirectoryCategory() {
    }
    
    public DirectoryCategory(java.lang.String fullViewableName,
                             java.lang.String specialEncoding) {
        this.fullViewableName = fullViewableName;
        this.specialEncoding = specialEncoding;
    }
    
    public java.lang.String getFullViewableName() {
        return fullViewableName;
    }
    
    public void setFullViewableName(java.lang.String fullViewableName) {
        this.fullViewableName = fullViewableName;
    }
    
    public java.lang.String getSpecialEncoding() {
        return specialEncoding;
    }
    
    public void setSpecialEncoding(java.lang.String specialEncoding) {
        this.specialEncoding = specialEncoding;
    }
}
リスト 4 生成されたDirectoryCategoryクラス

 

クライアントの作成

今回作るのは、テキストフィールドに検索のキーワードを入力し、Enterキーを入力するか検索ボタンを押すと Google Web APIをコールして、結果をウィンドウに出力するというものです。

作成したクライアントは次の4つのクラスからなります。

GoogleSearch クラス Google Web APIをコールするクラス
JGoogleBarクラス メインクラス 検索キーワードの入力
ResultViewクラス 検索結果を表示
SimpleBrowserクラス 検索されたWebページを表示

 

すべてのコードを示すのはスペースの関係上できませんが、残りのコードはここからダウンロードできます。

JAX-RPCを使用して Web サービスを利用するには次のような手順をふみます。

  1. Serviceインスタンスの生成。
  2. スタブの取得。
  3. スタブにはサービスのメソッドが定義されているので、それらのメソッドをコールする。
  4. 戻り値を受け取る。

実際には3.の手順でサーバとの通信が行われますが、それはJAX-RPCが自動的に行います。そのため、通信のことを気にしなくてもクライアントを作成することができます。

この手順を行っているのがGoogleSearchクラスです(リスト5)。

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
import google.webapi.*;
 
public class GoogleSearch {
    private GoogleSearchPort port;
 
    public static final int MAX = 100;
    public static final int MIN = 10;
 
    public GoogleSearch() {
        // 手順 1 サービスのインスタンスの生成
        GoogleSearchService service = new GoogleSearchService_Impl();
 
        // 手順 2 スタブの取得
        port = (GoogleSearchPort)service.getGoogleSearchPort();
    }
 
    public GoogleSearchResult search(String keyword, int start) throws RemoteException {
        return search(keyword, start, MAX);
    }
 
    public GoogleSearchResult search(String keyword, int start, int max) throws RemoteException {
        // 最大件数が MAX 以上だったら MAX に、MIN 以下であれば MIN にする
        if (max > MAX) {
            max = MAX;
        } else if (max < MIN) {
            max = MIN;
        }
 
        // 手順 3 Web サービスで定義されたメソッドのコール
        // 手順 4 戻り値の受け取り。
        GoogleSearchResult result
            = port.doGoogleSearch("XXXXXXXXXXXXXXXXXXXX", // java.lang.String key
                                  keyword, //java.lang.String q
                                  start, // int start
                                  10, // int maxResults
                                  false, //boolean filter
                                  "", // java.lang.String restrict        
                                  false, //boolean safeSearch                
                                  "lang_ja|lang_en", // java.lang.String lr
                                  "UTF-8", // java.lang.String ie
                                  "UTF-8"); // java.lang.String oe
        
        double time = result.getSearchTime();
        int count = result.getEstimatedTotalResultsCount();
        count -= start;
 
        // 検索結果の件数が 10 件以上で、表示する最大が 10 以上であれば
        // 再検索を行い、結果をマージする
        if (count > 0 && max > 10) {
            max = (count > max) ? max : count;
 
            // ResultElement を保持させるためのリスト
            List elements = new ArrayList();
            elements.add(result.getResultElements());
 
            for (int i = 10; i < max; i += 10) {
                int m = (i + 10 < max) ? 10 : max - i;
 
                // 再検索
                GoogleSearchResult tmpResult
                    = port.doGoogleSearch("XXXXXXXXXXXXXXXXXXXX", 
                                          keyword, start + i, m,
                                          false, "", false, "lang_ja|lang_en",
                                          "UTF-8", "UTF-8");
                
                // 検索結果をリストに追加
                elements.add(tmpResult.getResultElements());
                time += tmpResult.getSearchTime();
            }
             
            // 検索でヒットしたページの総数を調べる
            int number = 0;
            for (int i = 0; i < elements.size(); i++) {
                number += ((ResultElement[])elements.get(i)).length;
            }
 
            // 配列にまとめて、result にセットする
            ResultElement[] resultElements = new ResultElement[number];
            number = 0;
            for (int i = 0; i < elements.size(); i++) {
                int length = ((ResultElement[])elements.get(i)).length;
                System.arraycopy((ResultElement[])elements.get(i), 0,
                                 resultElements, number, length);
                number += length;
            }
 
            result.setEndIndex(start + number);
            result.setSearchTime(time);
            result.setResultElements(resultElements);
        }
        
        return result;
    }
}
リスト 5 GoogleSearchクラス

 

まず、16行目で手順1のServiceの生成を行っています。Google Web APIの場合、javax.xml.rpc.Serviceインタフェースを派生させたGoogleSearchServiceインタフェースを使用します。実際に生成するのはGoogleSearchService_Implクラスです。

次にスタブの取得を行います。取得とはいうものの、実際にはスタブの生成を行っています。手順1のServiceインタフェースはスタブの生成を行うファクトリになります。

スタブはリモートにあるWebサービスが提供しているオペレーションのインタフェースになり、java.rmi.Remoteインタフェースを派生させたインタフェースとなります。Google Web APIではGoogleSearchPortクラスです。

スタブの取得にはgetGoogleSearchPortメソッドを使用します。

スタブが取得できたらGoogle Web APIのメソッドがコールできます。このサンプルでは「検索」ボタンがクリックされると、searchメソッドがコールされ検索を行います。GoogleSearchPortインタフェースにWSDLファイルで定義されているオペレーションがメソッドとして定義してあるので、それを普通のメソッドのようにコールするだけです。

37行目のdoGoogleSearchメソッドの引数は表2のWSDLファイルに定義されたものと同じです。XXXXXXXXXXの部分は皆さんのライセンスキーをいれてください。

ここではフィルタはオフにしてあり、国や特定トピック検索は使用していません。セーフサーチもオフ、言語は日本語と英語を使用、文字コードは入出力ともUTF-8です。

メソッドは何度でもコールすることができるので、再検索も可能です。

戻り値はGoogleSearchResultクラスのオブジェクトになります。GoogleSearchResultクラスもWSDLファイルの型定義から生成されたクラスです。前述したDirectoryCategoryのようにgetterメソッドが定義されてあるので、これを利用して必要な情報を得ることができます。

通常はこれでいいのですが、Google Web APIでは一度に検索できる件数が10件までなので、すこし使い勝手がよくありません。そこで、10件以上の結果がある場合は再検索を行い、検索結果をまとめてしまいましょう。

それを行っているのがリスト5のグレーの部分です。

件数がmaxになるまで再検索をくり返し、その結果をマージしています。

検索でヒットしたWebページの情報はGoogleSearchResult#getResultElementsメソッドを使用することで、ResultElementクラスの配列として取得できます。これをリストに追加し、最後に1つの配列に入れ直すということを行っています。

後は検索結果をGUIで表示します。表示はResultViewクラスで行っています。

ヒットしたWebページの情報を保持しているResultElementクラスは、Webページの一部をsnippetとして保持しています。これはブラウザで検索したときにタイトルの下に表示されるものと同じもので、HTMLが使用されています。

この情報を表示するためにHTMLが表示できるJEditorPaneクラスを使用しました。ResultViewクラスでこの処理を行っている部分をリスト6に示しました。

HTMLを作成しているのがmekeResultTextメソッドです。表示しているのはタイトル、概要 (snippet)、URL、ページのサイズです。それぞれResultElementクラスのgetTitle, getSnippet, getURL, getCachedSizeメソッドを使用して取得しています。

タイトルにはハイパーリンクも設定しました。ハイパーリンクの部分をクリックされるとHyperlinkEventが発生します。HyperlinkEventが発生したら、SimpleBrowserクラスでハイパーリンク先のWebページを表示するようにしてみました。

SimpleBrowserクラスもJEditorPaneクラスを使用してHTMLドキュメントを表示していますが、JEditorPaneクラスはHTML3.2までしか解釈できないので、ただしく表示できないページも多くあります。

    // HTML 表示のために JEditorPane を生成
    private JEditorPane makeResultPane(ResultElement[] elements) {
        JEditorPane pane = new JEditorPane("text/html", makeResultText());
        pane.setEditable(false);
 
        // ハイパーリンクの設定
        pane.addHyperlinkListener(new HyperlinkListener() {
                public void hyperlinkUpdate(HyperlinkEvent event) {
                    if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
                        hyperlinkActivated(event.getURL());
                    }
                }
            });

        return pane;
    }
 
    // 検索でヒットしたページの情報を HTML で表記する
    private String makeResultText(ResultElement[] elements) {
        StringBuffer buffer = new StringBuffer();

        for (int i = 0 ; i < elements.length ; i++) {
            buffer.append("<p><a href=\"");
            buffer.append(elements[i].getURL());
            buffer.append("\">");
            buffer.append(elements[i].getTitle());
            buffer.sppend("</a><br>");
            buffer.append(elements[i].getSnippet());
            buffer.append("<br><font color=#008000>");
            buffer.append(elements[i].getURL());
            buffer.append(" - ");
            buffer.append(elements[i].getCachedSize());
            buffer.append("</font></p>");
        }
 
        return buffer.toString();
    }
 
    // ハイパーリンクがクリックされたときの処理
    // 簡易ブラウザーを立ち上げる
    private void hyperlinkActivated(URL url) {
        new SimpleBrowser(frame, url).show();
    }
リスト 6 ResultViewクラスのHTML表示設定部分

 

コンパイルと実行

コンパイルにはJAX-RPCのライブラリが必要です。<JWSDP_HOME>\jaxrpc\libの下にあるjaxrpc-api.jar、jaxrpc-impl.jarをクラスパスに追加してコンパイルします。

  >javac -classpath jaxrpc-api.jar;jaxrpc-impl.jar;. *.java

実行には表5に示したJARファイルが必要です。数が多いのでAntなどのメイクツールを使用するか、バッチファイルにしたほうがいいかもしれません。

メインクラスはJGoogleBarクラスです。実行すると図5のようにテキストフィールドと「検索」ボタンだけのウィンドウが表示されます。検索キーワードを入力してEnterを入力するか、「検索」ボタンをクリックすると検索を開始します。検索結果が別ウィンドウに表示され、ヒットしたWebページのタイトルをクリックするとWeb画面が表示されます(図6)。

表 5 JAX-RPCの実行に必要なJARファイル

<JWSDP_HOME>\jwsdp-shared\lib\mail.jar
<JWSDP_HOME>\jwsdp-shared\lib\activation.jar

<JWSDP_HOME>\jaxp\lib\jaxp-api.jar
<JWSDP_HOME>\jaxp\lib\endorsed\dom.jar
<JWSDP_HOME>\jaxp\lib\endorsed\sax.jar
<JWSDP_HOME>\jaxp\lib\endorsed\xalan.jar
<JWSDP_HOME>\jaxp\lib\endorsed\xercesImpl.jar

<JWSDP_HOME>\jaxrpc\lib\jaxrpc-api.jar
<JWSDP_HOME>\jaxrpc\lib\jaxrpc-impl.jar
<JWSDP_HOME>\jaxrpc\lib\jaxrpc-spi.jar

<JWSDP_HOME>\saaj\lib\saaj-api.jar
<JWSDP_HOME>\saaj\lib\saaj-impl.jar

 

GoogleAPIDemo の実行結果
図 5 JGoogleBar の初期画面

 

GoogleAPIDemo の実行結果
図 6 JGoogleBar の検索結果表示

 

応用サンプル

JGoogleBarは単純にGoogle Web APIを利用しただけですが、Google Web APIは単なる検索以外にもいろいろな用途に使えそうです。GoogleのサイトにはWeb APIを使ったアイディアとして次のような項目が示されています。

そこで応用サンプルとして、Webブラウザからのでは使いにくい日付指定の検索と、指定したWebサイトにリンクしているページ数を調べるアプリケーションを作ってみました。

今日のGoogle

Googleでは日付の範囲を指定して検索することが可能です。ただし、日付というのはWebページがGoogleのデータベースに登録された日のことで、Webページが作成または更新された日とは異なることに注意してください。

ブラウザからの検索では「検索オプション」(英語ではAdvanced Search)のページから日付を指定することができますが、3ヶ月以内、6ヶ月以内、一年以内の3種類しか指定することができません。

直接ブラウザで日付を指定する場合はdaterange:を使用します。daterange:の書き方は次のようになります。

  daterange:<start_date>-<end_date>

例えばjavaについて2004年1月中に登録されたページを検索するには検索キーワードを次のように指定します。

  java daterange:2453004-2453035

日付が単に数字になっていますが、これは日付をユリウス日で記述しなければならないからです。

ユリウス日はBC4713年の1月1日UTCの正午を0とした通日の値です。時刻は少数で表されます。たとえば2000年1月1日0時は2451544.5日と表されます。daterange:には少数は使えないので、切り上げるか切り下げて整数を使用するようにします。

しかし、ユリウス日の計算は面倒くさいので、プログラムで行うようにしたのがTodaysGoogleクラスです。日付を入力させてもいいのですが、サンプルということで単純にJComboBoxクラスを使用して検索の日付を指定するようにしました。

TodaysGoogleクラスはJGoogleBarクラスを派生させて作成し、キーワード入力を行うフィールドの前に日付を指定するコンボボックスを表示させています (図7参照)。

リスト7にコンボボックスを追加している部分と、ユリウス日を計算している部分を示してあります。

コンボボックスのフィールドは定数datesで表わし、それに対応するように現時点からの差を表わす定数diffsを用意します。

通日を表わすにはDateクラスやCalendarクラスを表わすよ1970年1月1日からのミリ秒を表わすSystem#currentTimeMillisメソッドの方が適当です。

1日の長さは24×60×60×1,000=86,400,000msなので、System#currentTimeMillisメソッドで得た数値を86,400,000で割れば、1970年1月1日からの日数を得ることができます(厳密にはうるう秒があるので少しだけ異なりますが、誤差の範囲です)。

ユリウス日で1970年1月1日は2440588.5日なので、先ほど得られた値をこの値に加算すれば今日のユリウス日が求まります。

後はコンボボックスで指定された範囲を反映させた検索キーワードを作成します。

    private JComboBox dateBox;
    private static final String[] dates = {"今日の", "今週の", "今月の", "3ヶ月間の", "半年の", "今年の"};
    private static final long[] diffs = {1, 7, 31, 93, 186, 365};
    private static final String DATERANGE = " daterange:";
 
    public TodaysGoogle() {
        frame.setTitle("Today's Google");

        // コンボボックスの追加        
        dateBox = new JComboBox(dates);
        dateBox.setEditable(false);
        frame.getContentPane().add(dateBox, BorderLayout.WEST);
    }
 
    private String calculateJulianDate(String keyword) {
        long now = System.currentTimeMillis();
 
        // 1 日は 86400000 ミリ秒
        // 1970.1.1 はユリウス日では 2440588
        long today = now / 86400000 + 2440588;
 
        long diff = diffs[dateBox.getSelectedIndex()];
 
        return keyword + DATERANGE + (today - diff) + "-" + today;
    }
リスト 7 ユリウス日の算出 (TodaysGoogleクラス)

 

GoogleAPIDemo の実行結果
図 7 Today's Google

 

リンクチェック

次のサンプルは指定したWebページにリンクしているページ数の推移を調べるためのサンプルです。

Googleで検索結果の上位にくるようになると、Webサイトを訪れる人が増加するはずです。検索結果の上位にくるかどうかはGoogleが決めているPageRankという値が重要になります。PageRankが高い値であれば検索結果の上位に来る可能性が高くなります。

PageRankはGoogle Tool Barを使用すれば見ることができます。Google Tool BarはInternet Explorerで使用できます。図8の中央部分の緑のバーがPageRankを表わしています。

PageRankの算出方法は公開されていませんが、ある程度推測することができます。重要な要素としてページにリンクしているWebページ数があげられます。Google Tool Barでも調べたいWebページをブラウザで表示し、iマークのプルダウンメニューからリンク元を選ぶことで、リンクしているWebページを調べることができます(図8参照)。

リンクしているページ数を定期的に調べることで、そのサイトを見ている人の傾向をつかむこともできると思います。

しかし、これを手作業で行うことは、できるならやりたくありません。そこでGoogle Web APIを使用してリンクを調べてみましょう。

あるページにリンクしているWebページを調べるにはlink:を使用します。

  link:<リンクを調べるたいURL>

URLにはhttp://は含む必要はありません。

リンクを調べるサンプルとして作成したのが、GoogleLinkCheckerです(リスト8)。

GoogleLinkCheckerは引数を2つとり、一方がリンクを調べるURL、他方が結果を出力するCSVファイルです。CSVファイルの行は次のような形式にしました。

    日付, リンク数, 前回との差分

GoogleLinkCheckerの実行例をリスト9に示します。

GoogleLinkCheckerは引数のURLにlink:を付加して検索を行っています (19行目)。リンクしているページ数は検索結果のカウント数と同じなので、それを保存します (22行目)。

saveメソッドでは引数で指定されたCSVファイルを読みこんで、最後の行を取り出します。最後の行の第2項目は前回のリンク数なので、これとcountとの差分を計算し、ファイルの最後に日付とともにアペンドします(55行目)。

たかだか70行程度のプログラムですが、魅力あるWebページを作るためには効果的です。GoogleLinkCheckerではリンク数だけを調べましたが、リンクしているURLを調べることも可能です。また、あるキーワードを検索したときに、自分のWebページが何位に位置するかを定期的に調べることなどもできます。

SEO (Search Engine Optimization)を行うためのツールとして、Google Web APIはなかなか使えそうです。

import java.io.RandomAccessFile;
import java.io.IOException;
import java.rmi.RemoteException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
 
import google.webapi.*;
 
public class GoogleLinkChecker {
    private GoogleSearch searcher;
 
    public GoogleLinkChecker(String url, String filename) {
        searcher = new GoogleSearch();
 
        try {
            // 引数で指定された URL に対するリンクがあるかを調べる
            GoogleSearchResult result = searcher.search("link:" + url, 0, 10);
            save(filename, result.getEstimatedTotalResultsCount());
        } catch (RemoteException ex) {
            ex.printStackTrace();
        }
    }
 
    private void save(String filename, int count) {
        try {
            RandomAccessFile file = new RandomAccessFile(filename, "rw");
            String lastLine = null;
            while (true) {
                String str = file.readLine();
                if (str == null) {
                    break;
                } else {
                    lastLine = str;
                }
            }
 
            int lattestCount = 0;
            if (lastLine != null) {
                StringTokenizer tok = new StringTokenizer(lastLine, ",");
                String num = tok.nextToken();
                num = tok.nextToken().trim();
 
                lattestCount = Integer.parseInt(num);
            }
 
            DateFormat format = new SimpleDateFormat("yyyy/MM/dd");
            file.writeBytes(format.format(new Date()) + ", "
                            + count + ", " + (count - lattestCount) + "\n");
            file.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        if (args.length < 2) {
            System.err.println("Usage:");
            System.err.println("    java GoogleLinkChecker [URL] [csv file]");
            return;
        }
 
        new GoogleLinkChecker(args[0], args[1]);
    }
}
リスト 8 GoogleLinkCheckerクラス

2003/01/01, 3, 3
2003/02/01, 5, 2
2003/03/01, 5, 0
2003/04/01, 15, 10
2003/05/01, 23, 8
2003/06/01, 20, -3
リスト 9 GoogleLinkCheckerの出力例

 

GoogleAPIDemo の実行結果
図 8 Google Tool Bar

 

おわりに

本章ではWebサービスを使用してGoogleを使用するサンプルを作ってみましたが、いかがだったでしょうか。思っていたほど難しくなかったのではないでしょうか。面倒くさいところはJAX-RPCが行ってしまうので、簡単にWebサービスを使うことが可能です。

単に検索を助けるためのツールとしても役に立つと思いますが、他の使い方もありそうです。今回は検索以外の例として、リンク数を調べるサンプルを作成しました。このような使い方以外にも、さまざまな用途で使えそうです。たかが「検索」とあなどる事なかれというところでしょうか。

 

ソースのダウンロード

 

コラム AmazonのWebサービス

検索の雄がGoogleだとすれば、ショッピングサイトの雄はAmazonでしょう。そのAmazonもWebサービスを公開しており、Amazonが販売している商品の検索などを行うことができます。また、アメリカだけでなく日本、イギリス、ドイツのAmazonでもWebサービスを公開しています。もちろん、日本で売られているものを検索することもできます。

さらに特徴的なのはAmazonが行っているアソシエイト・プログラムとWebサービスを連動させることができるということです。アソシエイト・プログラムはAmazonが提供している紹介プログラムで、Amazonの商品の紹介を行うことで紹介料をもらえることができます。AmazonのWebサービスを利用したアプリケーションを公開することで、もしかしたらお小遣いまでもらえるかもしれません。

Amazon Webサービス
http://www.amazon.co.jp/exec/obidos/subst/associates/join/webservices.html

Amazon アソシエイト・プログラム
http://www.amazon.co.jp/exec/obidos/subst/associates/join/associates.html

AmazonのWebサービスを利用するにも登録(無料)が必要です。また、Googleと同じようにSDKもダウンロードすることができます。WSDLファイルは次のURLで公開されています。

http://soap.amazon.com/schemas3/AmazonWebServices.wsdl

Amazonでは多様な検索サービスを公開しています。たとえば、次に示すような検索を提供しています。

  • キーワード
  • 著者
  • ISBN
  • 俳優/監督
  • アーティスト
  • 製造業者
  • ウィッシュリスト

また、アメリカのAmazonではショッピングカートへの商品の追加や削除なども行うことができます。

AmazonのWebサービスはREST(注)とSOAPという2種類の方法で提供されています。RESTを使用した場合、CGIを利用して検索を行い、検索結果をXMLのドキュメントとして受け取ります。XSLTを使用することも可能です。

SOAPを使用した場合、クライアントの作成は基本的にGoogle Web APIと同様です。WSDLからスタブを作成し、スタブを利用してサービスを実行します。たとえば、キーワード検索はkeywordSearchRequestメソッドになり、引数がKeywordRequestクラス、戻り値がProductInfoクラスになります。

リスト10にキーワード検索を行うリストを示しました。

KeywordRequestクラスのコンストラクタで、検索に必要な条件を設定します。ここでは和書、結果の順序がセールスランクに指定しました。アソシエイト・プログラムに参加されている方はwebservices-20の部分をアソシエイトIDに変更します。

検索結果として検索件数、ページ数、タイトルを表示しています。

AmazonのWebサービスを使えば、Amazonを利用したセレクトショップを作ることなどが可能です。

(注) REST Representational State Transfer
http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

    public void search(String keyword) {
        AmazonSearchService service = new AmazonSearchService_Impl();
        AmazonSearchPort port = (AmazonSearchPort)service.getAmazonSearchPort();
 
        KeywordRequest request = new KeywordRequest(
            keyword,          // キーワード
            "1",              // 結果のページ
            "books-jp",       // 検索するカテゴリ books-jp は和書
            "webservices-20", // アソシエイトID なければ webservices-20
            "lite",           // 結果の形式 lite or heavy
            "XXXXXXXXXXXXXX", // デベロッパID
            "+salesrank",     // 結果の並び順
            "jp",             // ロケール
            ""                // 価格の指定
        );
        
        try {
            ProductInfo info 
                = port.keywordSearchRequest(request);
        
            System.out.println("Total Results: " + info.getTotalResults());
            System.out.println("Total Pages: " + info.getTotalPages());
            System.out.println();
        
            Details[] details = info.getDetails();
            for (int i = 0; i < details.length; i++) {
                System.out.println("Title: " + details[i].getProductName());
            }
        } catch (RemoteException ex) {
            ex.printStackTrace();
        }
    }
リスト 10 Amazon Web Services のサンプル (抜粋)