Wie erhalte ich den Webseiteninhalt aus einer WebView?


86

Unter Android habe ich eine WebView, die eine Seite anzeigt.

Wie erhalte ich die Seitenquelle, ohne die Seite erneut anzufordern?

Es scheint , WebViewsollte eine Art hat getPageSource()Methode , die einen String zurückgibt, aber leider nicht.

Wenn ich JavaScript aktiviere, welches JavaScript ist für diesen Aufruf geeignet, um den Inhalt abzurufen?

webview.loadUrl("javascript:(function() { " +  
    "document.getElementsByTagName('body')[0].style.color = 'red'; " +  
    "})()");  

Verwenden Sie das JQuery-Skript und die JS-Schnittstelle, um HTML-Inhalte aus der Webansicht abzurufen. window.interface.processHTML ($ ("body"). html ());
DroidBot


Sie können die Antwort natürlich in HTML mithilfe der HTTP-Anforderungen erhalten. Wenn jedoch auf einer Seite Postdaten geladen werden müssen (z. B. Benutzeranmeldeinformationen usw.), schlägt dieser Ansatz einfach fehl. Ich denke, so sollte es sein, denn wenn Sie es könnten, könnten Sie wahrscheinlich Ihre eigene Android-App für jede Website erstellen, und das wäre scheiße!

Antworten:


160

Ich weiß, dass dies eine späte Antwort ist, aber ich habe diese Frage gefunden, weil ich das gleiche Problem hatte. Ich glaube, ich habe die Antwort in diesem Beitrag auf lexandera.com gefunden. Der folgende Code ist im Grunde ein Ausschneiden und Einfügen von der Site. Es scheint den Trick zu tun.

final Context myApp = this;

/* An instance of this class will be registered as a JavaScript interface */
class MyJavaScriptInterface
{
    @JavascriptInterface
    @SuppressWarnings("unused")
    public void processHTML(String html)
    {
        // process the html as needed by the app
    }
}

final WebView browser = (WebView)findViewById(R.id.browser);
/* JavaScript must be enabled if you want it to work, obviously */
browser.getSettings().setJavaScriptEnabled(true);

/* Register a new JavaScript interface called HTMLOUT */
browser.addJavascriptInterface(new MyJavaScriptInterface(), "HTMLOUT");

/* WebViewClient must be set BEFORE calling loadUrl! */
browser.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageFinished(WebView view, String url)
    {
        /* This call inject JavaScript into the page which just finished loading. */
        browser.loadUrl("javascript:window.HTMLOUT.processHTML('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');");
    }
});

/* load a web page */
browser.loadUrl("http://lexandera.com/files/jsexamples/gethtml.html");

6
Beachten Sie, dass dies möglicherweise nicht der unformatierte HTML-Code der Seite ist. Der Seiteninhalt hat sich möglicherweise dynamisch über JavaScript geändert, bevor er onPageFinished()ausgeführt wurde.
Paul Lammertsma

3
Es ist großartig, aber die Methode Aufruf browser.loadUrlin onPageFinishedverursacht onPageFinishedwieder aufgerufen werden. Möglicherweise möchten Sie vor dem Anruf überprüfen, ob es sich um den ersten Anruf handelt onPageFinishedoder nicht browser.loadUrl.
Yi H.

Danke @Blundell Es hat bei mir funktioniert. Ich würde gerne wissen, wie dies als Service implementiert werden kann . Da ist ein Dienst ohne Layout und Webview zum Speichern der Ergebnisse. Gibt es eine Möglichkeit, die Daten in einem anderen Objekt als der WebView abzulegen, damit wir das Javascript einfügen können, um den resultierenden HTML-Code zu erhalten?
Totalys

@Totalys das ist noch einfacher String html = new Scanner(new DefaultHttpClient().execute(new HttpGet("www.the url")).getEntity().getContent(), "UTF-8").useDelimiter("\\A").next();(abgekürzt, um in einen Kommentar zu passen :-))
Blundell

1
Vergessen Sie nicht, runOnUiThread (new Runnable () {... in public void processHTML einzufügen.
CoolMind

34

Pro Ausgabe 12.987 , stürzt Blundell Antwort (zumindest auf meinem 2.3 VM). Stattdessen fange ich einen Aufruf von console.log mit einem speziellen Präfix ab:

// intercept calls to console.log
web.setWebChromeClient(new WebChromeClient() {
    public boolean onConsoleMessage(ConsoleMessage cmsg)
    {
        // check secret prefix
        if (cmsg.message().startsWith("MAGIC"))
        {
            String msg = cmsg.message().substring(5); // strip off prefix

            /* process HTML */

            return true;
        }

        return false;
    }
});

// inject the JavaScript on page load
web.setWebViewClient(new WebViewClient() {
    public void onPageFinished(WebView view, String address)
    {
        // have the page spill its guts, with a secret prefix
        view.loadUrl("javascript:console.log('MAGIC'+document.getElementsByTagName('html')[0].innerHTML);");
    }
});

web.loadUrl("http://www.google.com");

17

Dies ist eine Antwort, die auf jluckyivs basiert , aber ich denke, es ist besser und einfacher, Javascript wie folgt zu ändern.

browser.loadUrl("javascript:HTMLOUT.processHTML(document.documentElement.outerHTML);");

6

Haben Sie darüber nachgedacht, den HTML-Code separat abzurufen und dann in eine Webansicht zu laden?

String fetchContent(WebView view, String url) throws IOException {
    HttpClient httpClient = new DefaultHttpClient();
    HttpGet get = new HttpGet(url);
    HttpResponse response = httpClient.execute(get);
    StatusLine statusLine = response.getStatusLine();
    int statusCode = statusLine.getStatusCode();
    HttpEntity entity = response.getEntity();
    String html = EntityUtils.toString(entity); // assume html for simplicity
    view.loadDataWithBaseURL(url, html, "text/html", "utf-8", url); // todo: get mime, charset from entity
    if (statusCode != 200) {
        // handle fail
    }
    return html;
}

2
Dies wird die Cookies nicht tragen.
Keith Adler

1
Dieser Ansatz löst CAPTCHA Dialog
Hector

4

Ich habe es geschafft, dies mit dem Code aus @ jluckyivs Antwort zum Laufen zu bringen, aber ich musste der processHTML-Methode im MyJavaScriptInterface eine Annotation @JavascriptInterface hinzufügen.

class MyJavaScriptInterface
{
    @SuppressWarnings("unused")
    @JavascriptInterface
    public void processHTML(String html)
    {
        // process the html as needed by the app
    }
}

1

Sie müssen die Methode auch mit @JavascriptInterface mit Anmerkungen versehen, wenn Ihre targetSdkVersion> = 17 ist. Da SDK 17 neue Sicherheitsanforderungen enthält, müssen alle Javascript-Methoden mit @JavascriptInterface mit Anmerkungen versehen werden. Andernfalls wird der folgende Fehler angezeigt: Nicht erfasster Typ Fehler: Objekt [Objekt Objekt] hat keine Methode 'processHTML' bei null: 1


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.