利用UIWebView抓取网页元素

文章目录
  1. 前言
  2. 背景信息
    1. 说明
    2. 意义
  3. 实现和原理

我们在开发中,可能需要分别提取网页中的部分数据,比如标签内的标题,或者是网页的描述信息,对于摘要抓取有一套非常深入的算法来做,通过查阅相关文献资料可以知道目前国外做的比较好,可能由于英文的语义分析较为简单的原因。

前言

其实这个方法是借道Javascript代码来完成的。因为不同的网页有不同的编码格式,通过加载网页源代码,再分析源代码内容达成目标的方法还存在一些问题,部分网页的源代码获取失败,我正在寻找办法来做。比如百度文库的网页,用iOS原生API加载源代码的时候就发生了错误,是encoder的问题。

本文通过以获取网页元数据、标签的开始,简单认识下将JS代码嵌入UIWebView的特性,展开说明在iOS开发中对UIWebView调用JS命令从而实现对网页控制的强大功能。

背景信息

说明

当然我们不通过深入研究中文语义分析等算法也可以通过网页源码抓取到一些网页的元素,比如在标签内,我们可以直接抓取到网页的标题;在<meta ...="">标签内,我们可以获取多种元数据。

关于meta标签:元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词。标签位于文档的头部,不包含任何内容。 标签的属性定义了与文档相关联的名称/值对。

比如则表明这个网页的『keywords』关键词为”Objective-C,iOS,CoreBluetooth”,表明这个网页的描述内容是”iOS蓝牙开发的Objective-C实现”。当然,这些meta信息都是可选的,很多网页是没有这些元信息的。具体的实现方式是利用UIWebView的Javascript可内联HTML特性抓取网页中的元素。

意义

大家可以尝试一下,给手机QQ在线的QQ号发送一个网址,如果这个网址具备这些信息,那么对方看到的将不是一串简单的网址,而是一个展示了页面title和描述信息,甚至还有一些页面中的图片。这就是我们做的这个小轮子的用处了。

实现和原理

首先创建UIWebView实例对象,并将该webView添加到视图中:

1
2
UIWebView *catcherView = [[UIWebView alloc] init];
[self.view addSubview:catcherView];

由于涉及到异步网络请求,我们需要实现UIWebViewDelegate协议:

1
@interface SKWebElementsCatcherView ()<UIWebViewDelegate>

实现该协议的相关方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma mark - UIWebViewDelegate
- (void)webViewDidStartLoad:(UIWebView *)webView {
NSLog(@"wait");
// 在这里处理加载动画等
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *jsSource = @"document.getElementsByTagName('title')[0].innerHTML";
NSString *htmlString = [self.catcherWebView stringByEvaluatingJavaScriptFromString:jsSource];
[self.catcherWebView stopLoading];
NSLog(@"%@",htmlString);
// 在这里告知视图完成加载
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(@"%@",error.description);
// 在这里处理错误
}

上面这段代码我们可以清晰的看出来,对网页元素的抓取是利用Javascript的方法完成的,这正是这个方法的精妙之处,利用UIWebView可以嵌入Javascript命令的特性,从而直接输出了网页的title。

我们要注意的是,在- (void)webViewDidFinishLoad:(UIWebView *)webView方法中jsSource的内容正是Javascript命令,这条语句中getElementsByTagName的意思就是通过标签名获取元素,后面的’title’参数即为网页标题的标签

那么如何获取网页的description呢?description是写在meta里面的,meta标签的内容可不能这么简单的获取了。我本人对Javascript也只是稍有了解,最终,我实现了抓取description乃至控制整个网页的目的。

原理同上,同样是由UIWebView- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script方法,将Javascript命令嵌入网页中,抓取数据。说到这,懂前端的人应该明白了,JS有多强大不用我说,别说抓个description了,那岂不是想做啥就做啥了。

分别让UIWebView嵌入如下几句Javascript代码:

1
2
3
var meta = document.getElementsByTagName('meta');
var result;
for(i in meta){if(typeof meta[i].name!="undefined"&&meta[i].name.toLowerCase()=="description"){share_desc = meta[i].content;}}"

这段代码是先获取到meta标签,再通过遍历meta标签,排除undefined并找到name为description的meta标签,最终得到description里的content。将以上JS代码嵌入UIWebView的方式就是一句一句的嵌入:

1
2
3
4
5
6
NSString *jsSource0 = @"var meta = document.getElementsByTagName('meta');";
NSString *htmlString = [self.catcherWebView stringByEvaluatingJavaScriptFromString:jsSource0];
NSString *jsSource1 = @"var result;";
htmlString = [self.catcherWebView stringByEvaluatingJavaScriptFromString:jsSource1];
NSString *jsSource2 = @"for(i in meta){if(typeof meta[i].name!=\"undefined\"&&meta[i].name.toLowerCase()==\"description\"){share_desc = meta[i].content;}}";
htmlString = [self.catcherWebView stringByEvaluatingJavaScriptFromString:jsSource2];

要注意,需要嵌入的JS代码是包含双引号的,在Objective-C的字符串中,是对双引号敏感的,所以我们需要在把JS代码写进Objective-C的字符串的时候,在双引号前面添加\,表明这是转义字符,这样才能正确识别双引号。

嵌入多句UIWebView,和在浏览器的console里敲代码是没区别的,同样是一步一步的提交。对于for循环和if判断,我们只需要在不破坏语法的情况下写进一行就可以了。

获取其他的标签元素,同理,我的JS水平仅仅是认知阶段,至于JS还能做什么,那就全看大家想象了。