1. 程式人生 > >android:webview獲取網頁登陸賬號和密碼

android:webview獲取網頁登陸賬號和密碼

使用webview獲取html頁面資訊

需求:抓取webview開啟的頁面中登陸資訊,簡單點說就是獲取第三方的賬號和密碼。(咋一想,這尼瑪有點坑啊,獲取別人的資訊,怎麼都不太好吧。但是也得實現呀。。。)

本文將以抓取百度賬號資訊為例。(這尼瑪也是一個坑。。。百度還是有一些安全措施的。自己挖的坑,笑著也要填完。)
這裡寫圖片描述

下面開始正文

測試地址

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

佈局程式碼

<android.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"> <WebView android:id="@+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" /> <android.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"> <TextView android:id="@+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" /> <TextView android:id="@+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" /> </android.support.constraint.ConstraintLayout>

這裡寫圖片描述

如果抓取成功,我們就可以看到 賬號密碼會在底部的textview中顯示。
這裡寫圖片描述

先來一波分析。我們要獲取頁面的資訊,那是不是要先抓取頁面再說呢?好,抓取頁面的方法非常簡單,直接百度就找到了,嘿嘿。。。

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連結,然後做下一步動作。
這裡寫圖片描述

上面是開啟登陸頁面的日誌,還有一些沒放到圖中。最開始打算在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,最好是在跳轉過程中獲取,這樣就不會有問題。
這裡寫圖片描述
獲取賬號密碼的方法如下,在方法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