【Android】WebViewに表示中のHTMLソースを取得する【最終版】
POSTED BY
2024-04-28
2024-04-28

ネットに落ちてる情報1つではダメで、いくつか組み合わせないと実現しなかった。
プロジェクト一式はこちら GetWebViewHtml
オリジナルのWebViewClientクラスを作ってviewSource関数で受信するのが定番だが、
@JavascriptInterfaceプロトコルをつけなくては動作しない。
さらに明示的なimport文が必要なのがハマりどころ。Auto Importの機能は重要。
import android.webkit.JavascriptInterface;
@JavascriptInterface
public void viewSource(final String src) {
作ったサンプルがこちら。
| Java | MainActivity.java | GitHub Source |
// アクティビティ
package net.servernote.getwebviewhtml;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.TextView;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends Activity implements View.OnClickListener,
TextView.OnEditorActionListener, View.OnFocusChangeListener {
public HashMap<String, Integer> N;
public HashMap<String, String> S;
private WebView WEBVIEW;
private EditText KEYWORD;
private Handler HANDLER = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//プリファレンスのロード
N = new HashMap<String, Integer>();
S = new HashMap<String, String>();
N.put("FILE_COUNTER", 0);
S.put("KEYWORD", "");
S.put("LAST_WEB_URL", "https://www.google.co.jp/");
SharedPreferences pref = getSharedPreferences("Preferences", MODE_PRIVATE);
for (Map.Entry<String, Integer> entry : N.entrySet()) {
entry.setValue(Integer.parseInt(pref.getString(entry.getKey(), entry.getValue() + "")));
}
for (Map.Entry<String, String> entry : S.entrySet()) {
entry.setValue(pref.getString(entry.getKey(), entry.getValue()));
}
//レイアウト
setContentView(R.layout.main);
KEYWORD = findViewById(R.id.keyword);
KEYWORD.setText(S.get("KEYWORD"));
KEYWORD.setOnEditorActionListener(this);
KEYWORD.setOnFocusChangeListener(this);
findViewById(R.id.search).setOnClickListener(this);
WEBVIEW = findViewById(R.id.webview);
WEBVIEW.getSettings().setJavaScriptEnabled(true);
WEBVIEW.setWebViewClient(new ViewSourceClient());
WEBVIEW.addJavascriptInterface(this, "activity");
WEBVIEW.loadUrl(S.get("LAST_WEB_URL"));
}
@Override
protected void onDestroy() {
//プリファレンスのセーブ
SharedPreferences pref = getSharedPreferences("Preferences", MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
for (Map.Entry<String, Integer> entry : N.entrySet()) {
editor.putString(entry.getKey(), entry.getValue() + "");
}
for (Map.Entry<String, String> entry : S.entrySet()) {
editor.putString(entry.getKey(), entry.getValue());
}
editor.commit();
super.onDestroy();
}
@Override
public void onClick(View view) {
int id = view.getId();
switch (id) {
case R.id.search:
doSearch();
break;
default:
break;
}
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
doSearch();
}
return true; // falseを返すと, IMEがSearch→Doneへと切り替わる
}
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard();
}
}
public void hideKeyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(KEYWORD.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
public void doSearch() {
hideKeyboard();
String str = KEYWORD.getText().toString();
S.put("KEYWORD", str);
if (str.length() <= 0) return;
try {
String enc = URLEncoder.encode(str, "UTF-8");
String url = "https://www.google.co.jp/search?q=" + enc;
S.put("LAST_WEB_URL", url);
WEBVIEW.loadUrl(url);
} catch (Exception e) {
Log.e("doSearch", e.toString());
}
}
@JavascriptInterface
public void viewSource(final String src) {
HANDLER.post(new Runnable() {
@Override
public void run() {
int counter = N.get("FILE_COUNTER");
counter++;
String file = "output." + counter + ".html";
N.put("FILE_COUNTER", counter);
try {
writeTextFile(file, src);
Log.d("viewSource", "saved to " + file);
} catch (Exception e) {
Log.e("viewSource", e.toString());
}
}
});
}
public String readTextFile(String file) throws Exception {
String ret = "";
FileInputStream in = openFileInput(file);
byte[] buf = new byte[in.available()];
in.read(buf);
ret = new String(buf).trim();
in.close();
return ret;
}
public void writeTextFile(String file, String text) throws Exception {
FileOutputStream out = openFileOutput(file, Context.MODE_PRIVATE);
out.write(text.getBytes());
out.close();
}
private static class ViewSourceClient extends WebViewClient {
@Override
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript:window.activity.viewSource(document.documentElement.outerHTML);");
}
}
} //end of class
・MainViewのエディットフィールドにキーワードを入力して「検索」すると、WebViewに渡してGoogleの検索結果を表示します。
・WebViewにはViewSourceClientサブクラスを設定してあるのでJavaScriptでviewSource関数にページソースが渡ります。
・渡ってきたソースは連番をつけてローカルファイルに保存します。output.1.html,output.2.htmlなどと増えていきます。
・adb shellなどで確認してください。
adb shell run-as net.servernote.getwebviewhtml ls adb shell run-as net.servernote.getwebviewhtml ls files adb shell run-as net.servernote.getwebviewhtml cat files/output.1.html > output.1.html
↑ローカルにファイルが保存され、中が確認できます。
Android 8以上の場合、shell を exec-out に置き換えます。
その他リソースファイル等はこちらGetWebViewHtml
・ソース連番、入力値、WebViewの検索URLをそれぞれPreferencesに保存し、onDestroyで書き込みしている。
次回のonCreateでそれらをロードし、Viewとフィールドの初期値としている。Preferencesは数値と文字列を分けたHashMapを使うと便利。
・検索アクションや失フォーカスのタイミングでソフトキーボードを閉じている。
Android
iPhone/iPad
Flutter
MacOS
Windows
Debian
Ubuntu
CentOS
FreeBSD
RaspberryPI
HTML/CSS
C/C++
PHP
Java
JavaScript
Node.js
Swift
Python
MatLab
Amazon/AWS
CORESERVER
Google
仮想通貨
LINE
OpenAI/ChatGPT
IBM Watson
Microsoft Azure
Xcode
VMware
MySQL
PostgreSQL
Redis
Groonga
Git/GitHub
Apache
nginx
Postfix
SendGrid
Hackintosh
Hardware
Fate/Grand Order
ウマ娘
将棋
ドラレコ
※本記事は当サイト管理人の個人的な備忘録です。本記事の参照又は付随ソースコード利用後にいかなる損害が発生しても当サイト及び管理人は一切責任を負いません。
※本記事内容の無断転載を禁じます。
※本記事内容の無断転載を禁じます。
【WEBMASTER/管理人】
自営業プログラマーです。お仕事ください!ご連絡は以下アドレスまでお願いします★
【キーワード検索】
【最近の記事】【全部の記事】
LetsEncrypt/certbotの証明書自動更新がエラーになる場合Wav2Lipのオープンソース版を改造して外部から呼べるAPI化する
Wav2Lipのオープンソース版で静止画の口元のみを動かして喋らせる
【iOS】アプリアイコン・ロゴ画像の作成・設定方法
オープンソースリップシンクエンジンSadTalkerをAPI化してアプリから呼ぶ【2】
オープンソースリップシンクエンジンSadTalkerをAPI化してアプリから呼ぶ【1】
【Xcode】iPhone is not available because it is unpairedの対処法
【Let's Encrypt】Failed authorization procedure 503の対処法
【Debian】古いバージョンでapt updateしたら404 not foundでエラーになる場合
ファイアウォール内部のWindows11 PCにmacOS Sequoiaからリモートデスクトップする
【人気の記事】【全部の記事】
【Windows10】リモートデスクトップ間のコピー&ペーストができなくなった場合の対処法進研ゼミチャレンジタッチをAndroid端末化する
Windows11+WSL2でUbuntuを使う【2】ブリッジ接続+固定IPの設定
VirtualBoxの仮想マシンをWindows起動時に自動起動し終了時に自動サスペンドする
【C/C++】小数点以下の切り捨て・切り上げ・四捨五入
GitLabにHTTPS経由でリポジトリをクローン&読み書きを行う
タスクスケジューラで変更を適用できません。ユーザーアカウントが不明であるか、パスワードが正しくないか、またはユーザーアカウントにタスクを変更する許可がありません。と出た
Intel MacBookAir2020にWindows10→Windows11をインストールする
Linuxでtar.xz形式のファイルを解凍する
Windows11のコマンドプロンプトでテキストをコピーする
【カテゴリーリンク】
Android
iPhone/iPad
Flutter
MacOS
Windows
Debian
Ubuntu
CentOS
FreeBSD
RaspberryPI
HTML/CSS
C/C++
PHP
Java
JavaScript
Node.js
Swift
Python
MatLab
Amazon/AWS
CORESERVER
Google
仮想通貨
LINE
OpenAI/ChatGPT
IBM Watson
Microsoft Azure
Xcode
VMware
MySQL
PostgreSQL
Redis
Groonga
Git/GitHub
Apache
nginx
Postfix
SendGrid
Hackintosh
Hardware
Fate/Grand Order
ウマ娘
将棋
ドラレコ