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
メソッドを呼び出しています。
最後に
ほかにも、実現する方法はあるかもしれません。色々試してみると面白いですね。