Come ottengo il contenuto della pagina web da una WebView?


86

Su Android, ho un WebViewche sta visualizzando una pagina.

Come ottengo l'origine della pagina senza richiedere nuovamente la pagina?

Sembra che WebViewdovrebbe avere un qualche tipo di getPageSource()metodo che restituisce una stringa, ma purtroppo non lo fa.

Se abilito JavaScript, qual è il JavaScript appropriato da inserire in questa chiamata per ottenere i contenuti?

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

usa lo script jquery e l'interfaccia js per ottenere il contenuto html da webview window.interface.processHTML ($ (\ "body \"). html ());
DroidBot


Ovviamente puoi ottenere la risposta in HTML utilizzando le Richieste HTTP, ma se alcune pagine richiedono il caricamento dei dati dei post (come ad esempio le credenziali dell'utente, ecc.), Questo approccio semplicemente fallisce. Penso che sia come dovrebbe essere perché se tu potessi farlo, probabilmente potresti creare la tua app Android per qualsiasi sito Web e farebbe schifo!

Risposte:


161

So che questa è una risposta tardiva, ma ho trovato questa domanda perché avevo lo stesso problema. Penso di aver trovato la risposta in questo post su lexandera.com. Il codice seguente è fondamentalmente un taglia e incolla dal sito. Sembra fare il trucco.

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
Attenzione che questo potrebbe non essere l'HTML grezzo della pagina; il contenuto della pagina potrebbe essere cambiato dinamicamente tramite JavaScript prima onPageFinished()dell'esecuzione.
Paul Lammertsma

3
E 'bello, ma la chiamata al metodo browser.loadUrlin onPageFinishedcauserà onPageFinisheddi essere chiamato di nuovo. Potresti voler controllare se è la prima chiamata onPageFinishedo meno prima di chiamare browser.loadUrl.
Yi H.

Grazie @Blundell Ha funzionato per me. Mi piacerebbe sapere come potrebbe essere implementato come servizio . Poiché è un servizio senza layout e webview per memorizzare i risultati. C'è un modo per mettere i dati in qualche altro oggetto diverso dal webView in modo da poter mettere il javascript per ottenere il codice html risultante?
Totalys

@Totalys è ancora più semplice String html = new Scanner(new DefaultHttpClient().execute(new HttpGet("www.the url")).getEntity().getContent(), "UTF-8").useDelimiter("\\A").next();(abbreviato per adattarsi a un commento :-))
Blundell

1
Non dimenticare di inserire runOnUiThread (new Runnable () {... in public void processHTML.
CoolMind

34

Secondo il numero 12987 , la risposta di Blundell si arresta in modo anomalo (almeno sulla mia VM 2.3). Invece, intercetto una chiamata a console.log con un prefisso speciale:

// 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

Questa è una risposta basata su jluckyiv , ma penso che sia meglio e più semplice cambiare Javascript come segue.

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

6

Hai considerato di recuperare il codice HTML separatamente e quindi di caricarlo in una visualizzazione Web?

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
Questo non trasporterà i biscotti.
Keith Adler,

1
questo approccio attiva la finestra di dialogo CAPTCHA
Hector

4

Sono riuscito a farlo funzionare utilizzando il codice dalla risposta di @ jluckyiv ma ho dovuto aggiungere l'annotazione @JavascriptInterface al metodo processHTML in MyJavaScriptInterface.

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

1

È inoltre necessario annotare il metodo con @JavascriptInterface se targetSdkVersion è> = 17, poiché sono presenti nuovi requisiti di sicurezza nell'SDK 17, ovvero tutti i metodi javascript devono essere annotati con @JavascriptInterface. Altrimenti vedrai un errore come: Uncaught TypeError: Object [object Object] non ha il metodo 'processHTML' su null: 1


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.