美文网首页iOSiOS DeveloperiOS开发
iOS 详解WKWebview屏蔽广告,保存图片以及截取链接

iOS 详解WKWebview屏蔽广告,保存图片以及截取链接

作者: VeryView | 来源:发表于2017-06-20 14:45 被阅读600次

    (原创)

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

    相关文章

      网友评论

      本文标题:iOS 详解WKWebview屏蔽广告,保存图片以及截取链接

      本文链接:https://www.haomeiwen.com/subject/cbnoqxtx.html