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'; " +  
    "})()");  
Gregm
fonte
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");
jluckyiv
fonte
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");
durka42
fonte
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);");
Nagoya0
fonte
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;
}
larham1
fonte
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
    }
}
dr_sulli
fonte
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

javauser71
fonte
0

Se você estiver trabalhando no kitkat e superior, poderá usar as ferramentas de depuração remota do Chrome para localizar todas as solicitações e respostas que entram e saem de sua visualização na web e também o código-fonte html da página visualizada.

https://developer.chrome.com/devtools/docs/remote-debugging

onusopus
fonte