2010年01月20日

HttpClientとHttpCleanerでGoogle検索結果を解析する例

HttpCleanerの実力試験代わりに、Google検索結果を解析させてみる。

HttpClientでコンテンツを取得し、独自拡張したResponseHandler内でHttpCleanerを呼び出して云々するようなだいぶ適当なソースだけど、割と問題なく動いてくれた。


import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;


public class GoogleSearchCrawler {

public static void main( String[] args ) throws Exception {

// Googleの検索用URL
// 今回はプログラミングという言葉で検索
String url = "http://www.google.co.jp/search?&num=100&q=";
url += URLEncoder.encode( "プログラミング", "utf-8" );

// HttpClientでリクエスト
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet( url );
ResponseHandler<List<SearchModel>> handler = new GoogleSearchResponseHandler();
List<SearchModel> list = client.execute( httpGet, handler );

// 解析結果を見てみる
for( SearchModel model : list ) {
System.out.println( model.getTitle() );
System.out.println( model.getHref() );
System.out.println( model.getDescription() );
}

// 終了処理
client.getConnectionManager().shutdown();
}
}

/**
* Google検索の中身をHttpCleanerを使用して解析し、
* 検索結果のリンク先をList<String>で返す
*/
class GoogleSearchResponseHandler
implements ResponseHandler<List<SearchModel>> {

public List<SearchModel> handleResponse(final HttpResponse response)
throws HttpResponseException, IOException {
StatusLine statusLine = response.getStatusLine();

// ステータスが400以上の場合は、例外をthrow
if( statusLine.getStatusCode() >= 400 ) {
throw new HttpResponseException(statusLine.getStatusCode(),
statusLine.getReasonPhrase());
}

// contentsの取得
HttpEntity entity = response.getEntity();
String contents = EntityUtils.toString(entity);

// HtmlCleaner召還
HtmlCleaner cleaner = new HtmlCleaner();
TagNode node = cleaner.clean( contents );

// 解析結果格納用リスト
List<SearchModel> list = new ArrayList<SearchModel>();

// まずliを対象にする
TagNode[] liNodes = node.getElementsByName("li", true);
for( TagNode liNode : liNodes ) {
// クラスがgじゃなかったら、多分リンクじゃないので除外
if( !"g".equals( liNode.getAttributeByName( "class" ) ) )
continue;

SearchModel model = new SearchModel();

// タイトルの取得
TagNode h3Node = liNode.findElementByName("h3", false);
if( h3Node == null )
continue;
model.setTitle( h3Node.getText().toString() );

// URLの取得
TagNode aNode = h3Node.findElementByName("a", false);
if( aNode == null )
continue;
model.setHref( aNode.getAttributeByName("href") );

// 概要の取得
TagNode divNode = liNode.findElementByName("div", false);
if( divNode == null )
continue;
model.setDescription( divNode.getText().toString() );

list.add( model );
}

return list;
}
}

/**
* 検索結果格納クラス
*/
class SearchModel {
String href;
String title;
String description;
public String getHref() {
return href;
}
public void setHref(String href) {
this.href = href;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}


HttpCleanerはスクレイピングに対しては多少機能が足りないようにも見えるけど、さくっと使う上ではGoogle検索のような崩れたソースに対しても十分に有効に働いてくれるようで。

Jerichoとかと比べるとどうなんだろう。HtmlCleanerに不足を感じるようになったら手を伸ばしてみたいところだけど、当面、この子で問題なさそう。