Jak uzyskać zawartość strony internetowej z WebView?

86

W systemie Android mam, WebViewktóry wyświetla stronę.

Jak uzyskać źródło strony bez ponownego żądania strony?

Wydaje się, że WebViewpowinien mieć jakąś getPageSource()metodę zwracającą ciąg znaków, ale niestety tak nie jest.

Jeśli włączę JavaScript, jaki JavaScript powinien być umieszczony w tym wywołaniu, aby pobrać zawartość?

webview.loadUrl("javascript:(function() { " +  
    "document.getElementsByTagName('body')[0].style.color = 'red'; " +  
    "})()");  
gregm
źródło
użyj skryptu jquery i interfejsu js, aby pobrać zawartość html z webview window.interface.processHTML ($ (\ "body \"). html ());
DroidBot
Możesz oczywiście uzyskać odpowiedź w HTML za pomocą żądań HTTP, ale jeśli jakaś strona wymaga załadowania danych postu (na przykład dane logowania użytkownika itp.), To podejście po prostu zawodzi. Myślę, że tak powinno być, ponieważ gdybyś mógł to zrobić, prawdopodobnie możesz stworzyć własną aplikację na Androida dla dowolnej witryny, a to byłoby do niczego!

Odpowiedzi:

161

Wiem, że to późna odpowiedź, ale znalazłem to pytanie, ponieważ miałem ten sam problem. Myślę, że znalazłem odpowiedź w tym poście na lexandera.com. Poniższy kod to w zasadzie wycinanie i wklejanie z witryny. Wydaje się, że to działa.

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
źródło
6
Uważaj, może to nie być surowy kod HTML strony; zawartość strony mogła zmieniać się dynamicznie przez JavaScript przed onPageFinished()wykonaniem.
Paul Lammertsma
3
To świetnie, ale wywołanie metody browser.loadUrlw onPageFinishedspowoduje onPageFinishednazywać ponownie. Możesz chcieć sprawdzić, czy jest to pierwsze połączenie, onPageFinishedczy nie, zanim zadzwonisz browser.loadUrl.
Yi H.
Dzięki @Blundell Pomogło mi. Chciałbym wiedzieć, jak można to zaimplementować jako usługę . Ponieważ jest to usługa bez układu i widoku internetowego do przechowywania wyników. Czy istnieje sposób na umieszczenie danych w jakimś innym obiekcie innym niż webView, abyśmy mogli umieścić kod javascript, aby uzyskać wynikowy kod HTML?
Totalys,
@Totalys to jeszcze łatwiejsze String html = new Scanner(new DefaultHttpClient().execute(new HttpGet("www.the url")).getEntity().getContent(), "UTF-8").useDelimiter("\\A").next();(w skrócie, aby pasowało do komentarza :-))
Blundell,
1
Nie zapomnij wstawić runOnUiThread (new Runnable () {... do publicznego procesu voidHTML.
CoolMind
34

W numerze 12987 odpowiedź Blundella ulega awarii (przynajmniej na mojej maszynie wirtualnej 2.3). Zamiast tego przechwytuję wywołanie console.log ze specjalnym przedrostkiem:

// 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
źródło
17

To jest odpowiedź oparta na jluckyiv's , ale myślę, że lepiej i łatwiej jest zmienić JavaScript w następujący sposób.

browser.loadUrl("javascript:HTMLOUT.processHTML(document.documentElement.outerHTML);");
nagoya0
źródło
6

Czy rozważałeś pobranie kodu HTML osobno, a następnie załadowanie go do widoku internetowego?

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
źródło
2
To nie będzie zawierało ciasteczek.
Keith Adler
1
takie podejście uruchamia okno dialogowe CAPTCHA
Hector
4

Udało mi się to uruchomić, używając kodu z odpowiedzi @ jluckyiv, ale musiałem dodać adnotację @JavascriptInterface do metody processHTML w MyJavaScriptInterface.

class MyJavaScriptInterface
{
    @SuppressWarnings("unused")
    @JavascriptInterface
    public void processHTML(String html)
    {
        // process the html as needed by the app
    }
}
dr_sulli
źródło
1

Musisz również dodać adnotację do metody @JavascriptInterface, jeśli twoja targetSdkVersion jest> = 17 - ponieważ w SDK 17 są nowe wymagania bezpieczeństwa, tj. Wszystkie metody javascript muszą być opatrzone adnotacją @JavascriptInterface. W przeciwnym razie zobaczysz błąd taki jak: Uncaught TypeError: Object [object Object] nie ma metody „processHTML” o wartości null: 1

javauser71
źródło