Comment obtenir le contenu de la page Web à partir d'une WebView?

86

Sur Android, j'ai un WebViewqui affiche une page.

Comment obtenir la source de la page sans demander à nouveau la page?

Il semble qu'il WebViewdevrait avoir une sorte de getPageSource()méthode qui renvoie une chaîne, mais hélas ce n'est pas le cas.

Si j'active JavaScript, quel est le JavaScript approprié à mettre dans cet appel pour obtenir le contenu?

webview.loadUrl("javascript:(function() { " +  
    "document.getElementsByTagName('body')[0].style.color = 'red'; " +  
    "})()");  
Gregm
la source
utilisez le script jquery et l'interface js pour obtenir du contenu html à partir de la vue web window.interface.processHTML ($ (\ "body \"). html ());
DroidBot
Vous pouvez évidemment obtenir la réponse en HTML en utilisant les requêtes HTTP, mais si une page nécessite le chargement de données de publication (comme par exemple les informations d'identification de l'utilisateur, etc.), cette approche échoue tout simplement. Je pense que c'est ainsi que cela devrait être parce que si vous pouviez le faire, vous pouvez probablement créer votre propre application Android pour n'importe quel site Web et ce serait nul!

Réponses:

161

Je sais que c'est une réponse tardive, mais j'ai trouvé cette question parce que j'avais le même problème. Je pense avoir trouvé la réponse dans ce post sur lexandera.com. Le code ci-dessous est essentiellement un copier-coller du site. Cela semble faire l'affaire.

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
la source
6
Attention, cela peut ne pas être le HTML brut de la page; le contenu de la page peut avoir changé dynamiquement via JavaScript avant onPageFinished()son exécution.
Paul Lammertsma
3
Il est grand, mais d' appeler la méthode browser.loadUrlen onPageFinishedfera onPageFinishedà nouveau appelé. Vous voudrez peut-être vérifier s'il s'agit du premier appel onPageFinishedou non avant d'appeler browser.loadUrl.
Yi H.
Merci @Blundell Cela a fonctionné pour moi. J'aimerais savoir comment cela pourrait être mis en œuvre en tant que service . Depuis est un service sans mise en page et webview pour stocker les résultats. Existe-t-il un moyen de placer les données dans un autre objet différent de la vue Web afin que nous puissions mettre le javascript pour obtenir le code html résultant?
Totalys
@Totalys c'est encore plus facile String html = new Scanner(new DefaultHttpClient().execute(new HttpGet("www.the url")).getEntity().getContent(), "UTF-8").useDelimiter("\\A").next();(abrégé pour tenir dans un commentaire :-))
Blundell
1
N'oubliez pas d'insérer runOnUiThread (new Runnable () {... dans public void processHTML.
CoolMind
34

Par numéro 12987 , la réponse de Blundell plante (au moins sur ma machine virtuelle 2.3). Au lieu de cela, j'intercepte un appel à console.log avec un préfixe spécial:

// 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
la source
17

C'est une réponse basée sur jluckyiv , mais je pense qu'il est préférable et plus simple de changer Javascript comme suit.

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

Avez-vous envisagé de récupérer le code HTML séparément, puis de le charger dans une vue 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
la source
2
Cela ne portera pas les cookies.
Keith Adler
1
cette approche déclenche le dialogue CAPTCHA
Hector
4

J'ai réussi à faire fonctionner cela en utilisant le code de la réponse de @ jluckyiv mais j'ai dû ajouter l'annotation @JavascriptInterface à la méthode processHTML dans MyJavaScriptInterface.

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

Vous devez également annoter la méthode avec @JavascriptInterface si votre targetSdkVersion est> = 17 - car il y a de nouvelles exigences de sécurité dans le SDK 17, c'est-à-dire que toutes les méthodes javascript doivent être annotées avec @JavascriptInterface. Sinon, vous verrez une erreur comme: Uncaught TypeError: Object [object Object] n'a pas de méthode 'processHTML' à null: 1

javauser71
la source