Como obtenho o conteúdo da página da web de um WebView?


86

No Android, tenho um WebViewque exibe uma página.

Como obtenho o código-fonte da página sem solicitá-la novamente?

Parece que WebViewdeveria haver algum tipo de getPageSource()método que retorne uma string, mas infelizmente não.

Se eu habilitar o JavaScript, qual é o JavaScript apropriado para colocar nesta chamada para obter o conteúdo?

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

use o script jquery e a interface js para obter conteúdo html de webview window.interface.processHTML ($ (\ "body \"). html ());
DroidBot de


Obviamente, você pode obter a resposta em HTML usando HTTP Requests, mas se alguma página exigir que os dados da postagem sejam carregados (como por exemplo, credenciais de usuário, etc.), essa abordagem simplesmente falhará. Eu acho que é assim que deveria ser porque se você pudesse fazer isso, provavelmente você poderia fazer seu próprio aplicativo Android para qualquer site e isso seria péssimo!

Respostas:


161

Sei que essa é uma resposta tardia, mas encontrei essa pergunta porque tive o mesmo problema. Acho que encontrei a resposta neste post em lexandera.com. O código abaixo é basicamente um recortar e colar do site. Parece que funciona.

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
Esteja ciente de que este pode não ser o HTML bruto da página; o conteúdo da página pode ter mudado dinamicamente por meio de JavaScript antes de onPageFinished()ser executado.
Paul Lammertsma

3
É ótimo, mas chamar o método browser.loadUrlno onPageFinishedfará com que onPageFinishedpara ser chamado novamente. Você pode querer verificar se é a primeira chamada de onPageFinishedou não antes de ligar browser.loadUrl.
Yi H.

Obrigado @Blundell Funcionou para mim. Gostaria de saber como isso pode ser implementado como um serviço . Uma vez que é um serviço sem layout e webview para armazenar os resultados. Existe uma maneira de colocar os dados em algum outro objeto diferente do webView para que possamos colocar o javascript para obter o código html resultante?
Totalys

@Totalys é ainda mais fácil String html = new Scanner(new DefaultHttpClient().execute(new HttpGet("www.the url")).getEntity().getContent(), "UTF-8").useDelimiter("\\A").next();(abreviado para caber em um comentário :-))
Blundell

1
Não se esqueça de inserir runOnUiThread (new Runnable () {... em public void processHTML.
CoolMind

34

De acordo com a edição 12987 , a resposta de Blundell falha (pelo menos na minha VM 2.3). Em vez disso, intercepto uma chamada para console.log com um prefixo 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");

17

Esta é uma resposta baseada no jluckyiv , mas acho melhor e mais simples alterar o Javascript da seguinte maneira.

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

6

Você já pensou em buscar o HTML separadamente e, em seguida, carregá-lo em um webview?

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
Isso não levará os cookies.
Keith Adler

1
esta abordagem aciona o diálogo CAPTCHA
Hector

4

Consegui fazer isso funcionar usando o código da resposta de @jluckyiv, mas tive que adicionar a anotação @JavascriptInterface ao método processHTML no MyJavaScriptInterface.

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

1

Você também precisa anotar o método com @JavascriptInterface se seu targetSdkVersion for> = 17 - porque há novos requisitos de segurança no SDK 17, ou seja, todos os métodos javascript devem ser anotados com @JavascriptInterface. Caso contrário, você verá um erro como: Uncaught TypeError: Object [object Object] não tem nenhum método 'processHTML' em null: 1


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.