¿Cómo obtengo el contenido de la página web desde un WebView?

86

En Android, tengo un WebViewque muestra una página.

¿Cómo obtengo la fuente de la página sin volver a solicitarla?

Parece que WebViewdebería tener algún tipo de getPageSource()método que devuelva una cadena, pero lamentablemente no lo hace.

Si habilito JavaScript, ¿cuál es el JavaScript apropiado para poner en esta llamada para obtener los contenidos?

webview.loadUrl("javascript:(function() { " +  
    "document.getElementsByTagName('body')[0].style.color = 'red'; " +  
    "})()");  
Gregm
fuente
use el script jquery y la interfaz js para obtener contenido html desde webview window.interface.processHTML ($ (\ "body \"). html ());
DroidBot
Obviamente, puede obtener la respuesta en HTML utilizando las solicitudes HTTP, pero si alguna página requiere que se carguen los datos de la publicación (como, por ejemplo, las credenciales de usuario, etc.), este enfoque simplemente falla. Creo que así es como debería ser porque si pudieras hacerlo, probablemente puedas crear tu propia aplicación de Android para cualquier sitio web y eso sería una mierda.

Respuestas:

161

Sé que esta es una respuesta tardía, pero encontré esta pregunta porque tuve el mismo problema. Creo que encontré la respuesta en esta publicación en lexandera.com. El siguiente código es básicamente un proceso de cortar y pegar del sitio. Parece hacer el truco.

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");
jluckyiv
fuente
6
Tenga en cuenta que puede que no sea el HTML sin formato de la página; el contenido de la página puede haber cambiado dinámicamente a través de JavaScript antes de que onPageFinished()se ejecutara.
Paul Lammertsma
3
Es muy bueno, pero una llamada al método browser.loadUrlen onPageFinishedcausará onPageFinisheda ser llamado de nuevo. Es posible que desee comprobar si es la primera llamada onPageFinishedo no antes de llamar browser.loadUrl.
Yi H.
Gracias @Blundell Me funcionó. Me gustaría saber cómo podría implementarse esto como un servicio . Dado que es un servicio sin diseño y vista web para almacenar los resultados. ¿Hay alguna forma de poner los datos en algún otro objeto diferente de webView para que podamos poner el javascript para obtener el código html resultante?
Totalys
@Totalys eso es aún más fácil String html = new Scanner(new DefaultHttpClient().execute(new HttpGet("www.the url")).getEntity().getContent(), "UTF-8").useDelimiter("\\A").next();(abreviado para que quepa en un comentario :-))
Blundell
1
No olvide insertar runOnUiThread (new Runnable () {... en public void processHTML.
CoolMind
34

Según el número 12987 , la respuesta de Blundell falla (al menos en mi 2.3 VM). En cambio, intercepto una llamada a console.log con un prefijo especial:

// 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");
durka42
fuente
17

Esta es una respuesta basada en jluckyiv , pero creo que es mejor y más simple cambiar Javascript de la siguiente manera.

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

¿Ha considerado obtener el HTML por separado y luego cargarlo en una vista 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;
}
larham1
fuente
2
Esto no llevará las cookies.
Keith Adler
1
este enfoque activa el diálogo CAPTCHA
Héctor
4

Logré que esto funcionara usando el código de la respuesta de @ jluckyiv, pero tuve que agregar la anotación @JavascriptInterface al método processHTML en MyJavaScriptInterface.

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

También debe anotar el método con @JavascriptInterface si su targetSdkVersion es> = 17, porque hay nuevos requisitos de seguridad en el SDK 17, es decir, todos los métodos de JavaScript deben anotarse con @JavascriptInterface. De lo contrario, verá un error como: Uncaught TypeError: Object [object Object] no tiene el método 'processHTML' en nulo: 1

javauser71
fuente