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上点个赞, 万分感谢

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 前言 关于UIWebView的介绍,相信看过上文的小伙伴们,已经大概清楚了吧,如果有问题,欢迎提问。 本文是本系列...
    CoderLF阅读 8,852评论 2 12
  • 1、加载网页 WKWebView *webView = [[WKWebView alloc] initWithFr...
    LearningCoding阅读 2,988评论 0 2
  • 公司开始让做一个新iOS项目,由于苹果的更新需要每次发版本审核,没法像服务器一样实时更新,技术部就讨论出原生+HT...
    奶茶007阅读 1,741评论 16 9
  • http://www.cnblogs.com/mddblog/p/5281748.html 一、整体介绍 UIWe...
    F麦子阅读 1,150评论 0 2
  • 前言 上一篇专门讲解了WKWebView相关的所有类、代理的所有API。前篇文章地址:http://blog.cs...
    iwolfox阅读 1,060评论 1 1