iOS 詳解WKWebview屏蔽廣告,保存圖片以及截取鏈接

(原創)

2017-6-20

最近剛剛實現的功能, 分享一下經驗

(原諒文章沒有句號, 為了開發方便我設置的中文下使用英文標點).

自己做了一個比較簡單瀏覽器, 里面有模仿UC實現長按頁面屏蔽廣告的功能

保存網頁圖片和獲取網頁跳頁的鏈接都是基于上面做的擴展, 這里詳細講去除廣告




思路:

手動去除廣告主要有以下幾個步驟:

1, 獲取html上的觸摸事件

2, 捕獲觸摸事件返回的html坐標(注意, 是html上的坐標, 不是webview上的坐標)

3, 判斷手勢的同時根據坐標獲取html元素

4, 屏蔽元素

Demo鏈接在最下面

理論上, 可以屏蔽任何html元素, 包括某篇文章的標題, 甚至包括百度一下的按鈕, 當然也包括廣告, 因為你不知道每個網頁的廣告都是怎么構成的, 不如讓用戶自己來選擇屏蔽, 然后做緩存, 把這些頁面的用來屏蔽的js代碼緩存, 每到這個頁面就注入這些js, 這就實現了長期屏蔽廣告(html元素)的功能

(由于本人并非專業JS開發,? 有點興趣才去接觸, 以下有錯誤的地方希望看官諒解指正, 感激不盡!)

第一步: 獲取HTML上的觸摸事件

想要獲取HTML的觸摸事件, 必須了解一點OC與JS交互的知識

OC與JS做交互分為兩塊, 1, OC向HTML注入JS? ? ? 2, JS傳值給OC

1, OC向HTML注入JS

OC為什么要和JS做交互? 因為OC沒有辦法直接對HTML頁面進行操作(或許有,我不知道), 只能注入JS, 讓JS去操作HTML, WKWebView里OC注入JS的方法如下:

- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

2, JS傳值給OC

流程大概是這樣的:

乙想告訴甲一個秘密, 甲給乙一張白紙,? 乙在白紙上寫完了又還給甲, 甲拿到了紙就知道了乙想說什么

于是, 那張紙是關鍵, 上邊的過程在程序里是這么實現的:

OC給了JS一個對象, JS給這個對象添加了一個屬性還給了OC, OC根據這個對象拿到了JS想要傳的值

分步實現:

? (1)OC給了JS一個對象, 對象就叫TheData吧, 在注入對象的時候會順便簽一個代理, (注意: 相同的字符串只能在每個wkwebview對象里注入一次, 第二次就會崩潰)

[[self.WKwebView configuration].userContentController addScriptMessageHandler:self name:@"TheData"];

? (2)JS給這個對象添加了一個屬性還給了OC, 這是一段JS代碼, 可是原本HTML里面沒有這代碼啊, 怎么辦?往上看, 剛講完OC注入JS代碼

window.webkit.messageHandlers.TheData.postMessage('123456')

? (3)OC根據這個對象拿到了JS想要傳的值, 當JS里執行了上邊第二步的代碼之后, OC里就會調用一個代理, WKScriptMessageHandler, 這個代理是在第一步簽的, 代理方法如下;

#pragma mark - WKScriptMessageHandler

- (void)userContentController:(WKUserContentController *)userContentController

didReceiveScriptMessage:(WKScriptMessage *)message {

if ([message.name isEqualToString:@"TheData"]) {

NSLog(@"JS給你傳的值~~~%@", message.body);

//這里會打印123456

}

}

以上大體上就實現了OC與JS的交互

為了獲取HTML上的觸摸事件, 我們注入以下JS, 這些JS能對HTML的觸摸事件進行響應(比如開始觸摸, 滑動, 手指離開等), 并且獲取觸摸事件的坐標, 利用JS給OC傳值的方法進行回傳:

- (void)zhurujs{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

[[self.WKwebView configuration].userContentController addScriptMessageHandler:self name:@"TheData"];

});

NSString *js = @"document.ontouchstart=function(event){\

x=event.targetTouches[0].clientX;\

y=event.targetTouches[0].clientY;\

window.webkit.messageHandlers.TheData.postMessage('thewebview:touch:start:\'+x+\':\'+y);\

};\

document.ontouchmove=function(event){\

x=event.targetTouches[0].clientX;\

y=event.targetTouches[0].clientY;\

window.webkit.messageHandlers.TheData.postMessage('thewebview:touch:move:\'+x+\':\'+y);\

};\

document.ontouchcancel=function(event){\

window.webkit.messageHandlers.TheData.postMessage('thewebview:touch:cancel');\

;};\

document.ontouchend=function(event){\

window.webkit.messageHandlers.TheData.postMessage('thewebview:touch:end');\

};";

[_WKwebView evaluateJavaScript:js completionHandler:^(id _Nullable response, NSError * _Nullable error) {

NSLog(@"response: %@ error: %@", response, error);

NSLog(@"call js alert by native");

}];

}

第二步: 捕獲觸摸事件返回的html坐標

在JS傳值的代理里面這樣寫:

- (void)userContentController:(WKUserContentController *)userContentController

didReceiveScriptMessage:(WKScriptMessage *)message {

if ([message.name isEqualToString:@"TheData"]) {

[self jszhixing:message.body];

}

}

- (void)jszhixing:(NSString *)str{

//? ? NSLog(@"requestString == %@", str);

_components = nil;

_components = [str componentsSeparatedByString:@":"];

if ([_components count] > 1 && [(NSString *)[_components objectAtIndex:0]

isEqualToString:@"thewebview"]) {

if([(NSString *)[_components objectAtIndex:1] isEqualToString:@"touch"])

{

if ([(NSString *)[_components objectAtIndex:2] isEqualToString:@"start"])

{

float ptX = [[_components objectAtIndex:3]floatValue];

float ptY = [[_components objectAtIndex:4]floatValue];

NSLog(@"touch point (%f, %f)", ptX, ptY);

NSLog(@"開始觸摸");

}else if ([(NSString *)[_components objectAtIndex:2] isEqualToString:@"move"]){

float ptX = [[_components objectAtIndex:3]floatValue];

float ptY = [[_components objectAtIndex:4]floatValue];

NSLog(@"touch point (%f, %f)", ptX, ptY);

NSLog(@"手指移動");

}else if ([(NSString*)[_components objectAtIndex:2]isEqualToString:@"cancel"]) {

NSLog(@"取消");

}else if ([(NSString*)[_components objectAtIndex:2]isEqualToString:@"end"]) {

NSLog(@"手指離開");

}

}

}

}

上邊這個方法獲取到手指剛觸摸和移動時的HTML坐標

第三步: 根據坐標獲取html元素

document.elementFromPoint(%f, %f)

這句js就是獲取這個元素的代碼, 但是直接注入這個代碼返回值是空, 必須注入如:

獲取該元素class標簽對應的值

document.elementFromPoint(%f, %f).className

獲取該元素標簽值

document.elementFromPoint(%f, %f).tagName

第四步: 屏蔽元素

根據坐標, 獲取元素, 屏蔽元素, 都在一句代碼里

document.elementFromPoint(%f, %f).style.display = 'none'

實現

[_WKwebView evaluateJavaScript:[NSString stringWithFormat:@"document.elementFromPoint(%f, %f).style.display = 'none'", x, y] completionHandler:^(id _Nullable response, NSError * _Nullable error) {

}];

以上4個步驟就基本實現了,? 屏蔽任何HTML元素的功能

效果圖:




保存圖片

獲取元素的道理與上面的一樣, 都是通過點擊(或者長按)獲取元素, 獲取到元素時候取出他的tagName, 如果tagName是IMG, 那就再取他的src, 這就是網絡圖片的地址了, 用此方法可以取出網頁上任意圖片的地址, 根據圖片地址保存圖片到相冊就不多說了

[_WKwebView evaluateJavaScript:[NSString stringWithFormat:@"document.elementFromPoint(%f, %f).tagName", ptX, ptY] completionHandler:^(id _Nullable response, NSError * _Nullable error) {

NSString * tagName = response;

NSLog(@"tagName %@", tagName);

if ([tagName isEqualToString:@"IMG"]) {

[_WKwebView evaluateJavaScript:[NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", ptX, ptY] completionHandler:^(id _Nullable response, NSError * _Nullable error) {

//圖片地址

NSString * imgURL = response;

}];

}

}];



截取鏈接

獲取元素的道理同上, 鏈接一般都在A標簽里面, 那么如果我們要獲取某一個點擊的鏈接, 就要獲取對應元素的A標簽

獲取A標簽分為兩種情況:

第一種, 獲取的元素tagName是DIV, 如果你確定他有鏈接的活, 可以直接通過以下獲取A里面的鏈接

[_WKwebView evaluateJavaScript:[NSString stringWithFormat:@"document.elementFromPoint(%f, %f).getElementsByTagName(\"a\")[0].href", ptX, ptY] completionHandler:^(id _Nullable response, NSError * _Nullable error) {

NSString * href = response;

}];

第二種, 獲取的元素tagName不是DIV, 有可能是SPAN, H3之類的基礎標簽, 這種標簽一般都是嵌入進A標簽的, 你看他的父標簽(parentNode)的tagName是不是A, 不是再看他的父標簽的父標簽是不是A, 我最多做了三級判斷, 一般一級就可以直接取出來A,

下面放的的是一級標簽的判斷及取鏈接的代碼

[_WKwebView evaluateJavaScript:[NSString stringWithFormat:@"document.elementFromPoint(%f, %f).parentNode.tagName", ptX, ptY] completionHandler:^(id _Nullable response, NSError * _Nullable error) {

if ([response isEqualToString:@"A"]) {

[_WKwebView evaluateJavaScript:[NSString stringWithFormat:@"document.elementFromPoint(%f, %f).parentNode.href", ptX, ptY] completionHandler:^(id _Nullable response, NSError * _Nullable error) {

NSString * href = response;

}];

}

}];


總結:

善于利用JS注入解決一些OC難以解決的問題, 這是整篇文章的中心

Demo 地址:? https://github.com/yizhimaomaoqiu/-#-

看得開心的小伙伴幫幫忙去git上點個贊, 萬分感謝

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 前言 關于UIWebView的介紹,相信看過上文的小伙伴們,已經大概清楚了吧,如果有問題,歡迎提問。 本文是本系列...
    CoderLF閱讀 9,022評論 2 12
  • 1、加載網頁 WKWebView *webView = [[WKWebView alloc] initWithFr...
    LearningCoding閱讀 3,158評論 0 2
  • 公司開始讓做一個新iOS項目,由于蘋果的更新需要每次發版本審核,沒法像服務器一樣實時更新,技術部就討論出原生+HT...
    奶茶007閱讀 1,799評論 16 9
  • http://www.cnblogs.com/mddblog/p/5281748.html 一、整體介紹 UIWe...
    F麥子閱讀 1,262評論 0 2
  • 前言 上一篇專門講解了WKWebView相關的所有類、代理的所有API。前篇文章地址:http://blog.cs...
    iwolfox閱讀 1,123評論 1 1