AndroidのWebViewでHTMLソースを取得する
パッケージJava製品開発担当の大です。こんにちは。
最近、趣味でAndroidアプリの開発をやっています。
自分で使っている携帯端末で、自分の作ったアプリを手軽に動かせるというのは、率直に言ってとても楽しいですね。学生のころに、Z80のマイコンボードに打ち込んだプログラムが動いて、はじめてLEDが点灯したときのような感動があります。
さて、Androidに標準であるWebViewクラスを使用してウェブアクセスを行うアプリを作成していて、ちょっと困ったことがありました。WebViewクラスを使用してウェブにアクセスするのはとても簡単なのですが、アクセスしているウェブページのHTMLソースを取得することが出来ないのです。タイトルとかURLとかは取得できるんですけどね。そこで、HTMLソースを取得する方法を考えてみました。
方法1: iframeを使う
思いついたひとつめの方法は、iframeを持つHTMLファイルを用意して、iframe内にターゲットのページを読み込むというものです。こんな単純なHTMLです。
<html>
<head>
<script type="text/javascript">
function load(src, callback) {
var myframe = document.getElementById("myframe");
myframe.onload = function() {
window.activity[callback](myframe.contentDocument.documentElement.outerHTML);
}
myframe.src = src;
}
</script>
</head>
<body style="padding: 0px; margin: 0px">
<iframe id="myframe" src="" style="padding: 0px; margin: 0px; width:100%; height:100%">
</iframe>
</body>
</html>
ここで定義しているload関数は、引数で指定されたsrcをiframeで読み込む(9行目)のですが、読み込む前にiframeのonloadで細工をしています。onloadの中身を見る前に、このHTMLをロードする側を見てみましょう。
今回は、こんな適当な画面を用意しました。入力されたURLをボタンクリックでWebViewに表示、下のTextViewにそのHTMLソースを表示します。
Activityはこんな感じです。
public class TestActivity extends Activity implements OnClickListener {
private Handler handler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebView web = (WebView) findViewById(R.id.webView1);
web.getSettings().setJavaScriptEnabled(true);
web.getSettings().setBuiltInZoomControls(true);
web.loadUrl("file:///android_asset/iframe.html");
web.addJavascriptInterface(this, "activity");
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
EditText text = (EditText) findViewById(R.id.editText1);
String url = text.getText().toString();
WebView web = (WebView) findViewById(R.id.webView1);
web.loadUrl("javascript:load('" + url + "', 'viewSource')");
}
public void viewSource(final String src) {
handler.post(new Runnable() {
@Override
public void run() {
TextView text = (TextView) findViewById(R.id.textView1);
text.setText(src);
}
});
}
}
12行目でローカルのassetsフォルダに置いたHTMLを読み込みます。また、JavaScriptとの橋渡しをするインタフェースとして、このオブジェクトを「activity」という名前で登録しています(この名前はなんでもいいみたいです)。HTML側7行目の「window.activity」はこれのことです。
ボタンのonClickで、定義したJavaScriptのload関数を呼び出します(24行目)。第二引数に、viewSourceというのを指定していますね。これが、JavaScript側でコールバックとして呼び出されます。
あとは、viewSourceにiframe内のHTMLが引数で渡されてきますので、加工するなりスクレイピングするなり好きにすればいいと思います。ここではTextViewに流し込んでいます(描画スレッドではないので扱いには注意が必要です)。
方法2: WebViewClientを使う
ふたつめの方法は、WebViewClientを使用するものです。こちらの場合は特にローカルのHTMLを用意したりする必要はありません。Activityのソースは、方法1のものとほとんど同じです。
public class TestActivity extends Activity implements OnClickListener {
private Handler handler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebView web = (WebView) findViewById(R.id.webView1);
web.getSettings().setJavaScriptEnabled(true);
web.getSettings().setBuiltInZoomControls(true);
web.setWebViewClient(new ViewSourceClient());
web.addJavascriptInterface(this, "activity");
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
EditText text = (EditText) findViewById(R.id.editText1);
String url = text.getText().toString();
WebView web = (WebView) findViewById(R.id.webView1);
web.loadUrl(url);
}
public void viewSource(final String src) {
handler.post(new Runnable() {
@Override
public void run() {
TextView text = (TextView) findViewById(R.id.textView1);
text.setText(src);
}
});
}
private static class ViewSourceClient extends WebViewClient {
@Override
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript:window.activity.viewSource(document.documentElement.outerHTML);");
}
}
}
WebViewClientのサブクラスとして、ここではViewSourceClientというクラスを定義しています(37行目~)。12行目でこのクラスのインスタンスをWebViewに登録しています。ボタンのonClickでは、方法1のようにJavaScriptの関数を呼び出すのではなく、普通にloadUrlで指定されたURLをロードします(24行目)。ページが終了するとViewSourceClientのonPageFinishedが呼び出されます。その中で、ActivityのviewSourceメソッドを呼び出しています。
最後に
ほかにも、実現する方法はあるかもしれません。色々試してみると面白いですね。

