首页 > 学院 > 开发设计 > 正文

OC爬虫 -- 结合正则表达式

2019-11-06 09:50:20
字体:
来源:转载
供稿:网友

一、说明

自从成为一只猿,脑子里面总会有些奇奇怪怪的想法,例如,最近想写爬虫玩玩。一开始打算用Python写,但奈何Python功力不足,还需刻苦修炼;于是乎,退而求其次使用OC来暂时满足一下。

使用OC写爬虫是可以的,但其不足之处在于OC用于移动设备编程,而移动设备那小的可怜的存储空间(相对于PC),大多数场景并不适用。然,获取小量数据、做个小试验还是可以的。下面使用OC配合正则表达式获取某个网页上自己需要的内容。

二、获取  CSDN 官方频道  这个页面里面所有博文的标题和链接

进入这个页面后我们发现里面有很多模块和内容,其中就有我们需要的博文列表,那怎么每篇博文的标题和链接取出来呢,下面一步步处理:

1.打开 Firebug 插件(我用的Firefox,其他浏览器也有类似插件),可以看到页面的源码

2.当然,在进行后面的处理前是必须先将页面 down 下来的

//获取网页数据并转为字符串- (NSString *)htmlWithUrlString:(NSString *)urlString {        urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];        NSURL *url = [NSURL URLWithString:urlString];        NSURLRequest *request = [NSURLRequest requestWithURL:url];        NSError *error = nil;        if (error) {        NSLog(@"%@",error.localizedDescription);        return nil;    }        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:&error];    NSString *backStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];        return backStr;}

3.在 <body> xxx </body> 中寻找博文列表的内容,如果不好找,可以用 Firebug  右上角搜索器搜索某一篇文章标题,会容易许多

经过查找发现,所有文章(置顶文章除外)都在 <div id="article_list" class="list"> xxx </div>之间,所以我们就可以进行第一层过滤

/**     第一次过滤正则          截取取内容符合 <div id="article_list" class="list">...</div> 格式的字符串     */    NSString *firstPattern = [NSString stringWithFormat:@"<div id=/"article_list/" class=/"list/">(.*)</div>"];        NSString *content = [htmlStr firstMatchWithPattern:firstPattern];

4.通过上图我们还发现每篇文章的 title 和 url 都在 <span class="link_title"> <a href="/blogdevteam/article/details/xxxxxx"> xxx </a> </span> 之间,so,进行第二次过滤,获得需要的字符串,并将其转化为数组

/**     第二次过滤正则          截取取内容符合 <span class="link_title"><a href="...">...</a></span> 格式的字符串     */    NSString *secondPattern = @"<span class=/"link_title/"><a href=/"(.*?)/">(.*?)</a></span>";        //将字符串转为数组    NSArray *array = [content matchesWithPattern:secondPattern keys:@[@"url", @"title"]];

5.在得到文章列表数组后,却发现每篇文章的 title 前后存在空格与换行符,url 也不完整,对于强迫症的我来说这不能忍,嗯,下面就收拾他们

NSMutableArray *tempArray = [NSMutableArray array];        //去除title中的 空格 和 换行    //将url补全,方便后面使用    for (NSDictionary *dic in array) {                NSString *title = dic[@"title"];                NSMutableArray *arr = [NSMutableArray arrayWithArray:[title componentsSeparatedByString:@" "]];        [arr removeObject:@""];        [arr removeLastObject];        [arr removeObjectAtIndex:0];                NSString *newStr = [arr componentsJoinedByString:@" "];        NSDictionary *appDic = @{@"url":[NSString stringWithFormat:@"http://blog.csdn.net%@",dic[@"url"]],                               @"title":newStr};                [tempArray addObject:appDic];    }

6.在获取满意的数据后,我们就可以把它放在 UITableView 上展示了。此外,为了方便使用,把数据本地化保存是个不错的选择

存入本地

NSString *homePath = NSHomeDirectory();NSString *path = [homePath stringByAppendingPathComponent:@"Library/Caches/test_cache.plist"];    [tempArray writeToFile:path atomically:YES];

取出

- (NSArray *)getCacheArray{        NSString *homePath = NSHomeDirectory();    NSString *path = [homePath stringByAppendingPathComponent:@"Library/Caches/test_cache.plist"];    NSArray *cacheArr = [NSArray arrayWithContentsOfFile:path];        return cacheArr;}

7.为了完成以上处理,NSString 系统类提供的方法是不足的,所以需要对其进行扩展

/** 将GBK编码的二进制数据转换成字符串(这里没有使用) */+ (NSString *)UTF8StringWithHZGB2312Data:(NSData *)data{    NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);    return [[NSString alloc] initWithData:data encoding:encoding];}/** 查找并返回第一个匹配的文本内容 */- (NSString *)firstMatchWithPattern:(NSString *)pattern{    NSError *error = nil;    NSRegularExPRession *regex = [NSRegularExpression regularExpressionWithPattern:pattern                                                                           options:NSRegularExpressionCaseInsensitive | NSRegularExpressionDotMatchesLineSeparators                                                                             error:&error];        if (error) {        NSLog(@"匹配方案错误:%@", error.localizedDescription);        return nil;    }        NSTextCheckingResult *result = [regex firstMatchInString:self options:0 range:NSMakeRange(0, self.length)];        if (result) {        NSRange range = [result rangeAtIndex:0];        return [self substringWithRange:range];    } else {        NSLog(@"没有找到匹配内容 %@", pattern);        return nil;    }}/** 查找多个匹配方案结果 */- (NSArray *)matchesWithPattern:(NSString *)pattern{    NSError *error = nil;    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern                                                                           options:NSRegularExpressionCaseInsensitive | NSRegularExpressionDotMatchesLineSeparators                                                                             error:&error];        if (error) {        NSLog(@"匹配方案错误:%@", error.localizedDescription);        return nil;    }        return [regex matchesInString:self options:0 range:NSMakeRange(0, self.length)];}/** 查找多个匹配方案结果,并根据键值数组生成对应的字典数组 */- (NSArray *)matchesWithPattern:(NSString *)pattern keys:(NSArray *)keys{    NSArray *array = [self matchesWithPattern:pattern];        if (array.count == 0) return nil;        NSMutableArray *arrayM = [NSMutableArray array];        for (NSTextCheckingResult *result in array) {        NSMutableDictionary *dictM = [NSMutableDictionary dictionary];                for (int i = 0; i < keys.count; i++) {            NSRange range = [result rangeAtIndex:(i + 1)];                        [dictM setObject:[self substringWithRange:range] forKey:keys[i]];        }        [arrayM addObject:dictM];    }        return [arrayM copy];}三、补充

1.我们知道当CSDN博客每页所能呈现的文章数量是有限的,上面过程所获取的数据只是列表的第一页,也就是 :

    http://blog.csdn.net/blogdevteam

    而从第二页开始每页的链接为

    http://blog.csdn.net/blogdevteam/article/list/2

    http://blog.csdn.net/blogdevteam/article/list/3

    http://blog.csdn.net/blogdevteam/article/list/4

    ......

很明显,之后的页面链接在有规律的改变,所以我们可以用循环......

好吧,其实这里有个简单粗暴的方法 ----> 将页数直接取值 >= 该账号下文章最大分页数,如:http://blog.csdn.net/blogdevteam/article/list/999

因为,当页数 >=  最大分页数时,通过第一步下载下来的页面数据中将包含所有文章

2.上面的操作是没有包含置顶文章的,不过通过观察网页源码,我们可以发现置顶文章藏在 <div id="article_toplist" class="list"> xxx </div>中,既然知道了在哪里,那么在修改一个过滤正则,置顶文章也可以抓到手了。

最后,源码地址:  https://github.com/HuberyYang/SmallSpider.git

  


上一篇:最简单的驱动模块

下一篇:红点

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表