Fork me on GitHub

用Objective-C写一个简单的爬虫(一)

你是不是想写个爬虫来抓取点东西,但是你又不会Python或者别的语言,那么你就得感谢你自己能来到这里。接下来就交给你如何用Objective-C写一个爬虫。

什么?Objective-C也能写爬虫?不都是Python才可以的么?那么我告诉你基本上大部分语言都是可以写爬虫的,只是Python用来写爬虫比较方便,他有海量的三方库可以让开发者调用而已。而别的语言基本上就是靠自己苦逼的撸了。

那么用Objective-C写爬虫有什么用?靠iPhone来爬数据么?这里博主只是写个简单的爬虫而已,并不指望用此来专门抓取数据,你觉得让手机去爬数据能爬多少啊。本教程会兼容Mac软件开发,你可以用此技术开发mac爬虫工具。

废话少说,让我们来开始吧!

必备工具

  • 链接网络的Mac,
  • 浏览器,推荐用Google Chrome
  • Xcode开发工具

必备技能

  • 熟悉Objective-C
  • 能阅读HTML,能认识基本的标签即可
  • 正则表达式,大概浏览一下就行,这里在遇到的时候博主会简单介绍一下

本篇目标

分析网页

用浏览器打开回车桌面,并且按下 option + command + i打开开发者工具。
如图:

确保右上方是选中的Elements选项,下面HTML就是网页的源代码。鼠标在HTML上面移动的时候左侧页面会有相应的模块提示,我们以次点开标签,就能找到想要的图片地址:

如图:

粘一段代码:

1
2
3
4
5
6
<img a_width="576" a_height="360" 
src="https://up.enterdesk.com/edpic_360_360/cf/e1/8f/cfe18f5a7e0a42355784bb992f313853.jpg"
alt="清明节气"
title="清明节气"
style="height: 185px; width: 296px;"
>

上段代码中src就是网页中的图片地址,我们接下来就是把这个src的值提取出来。

编码

上面我们分析了网页源码,接下来我们已经知道了大致流程:

  • 获取网页源码
  • 正则提取url

好! 让我们来创建一个Objective-C项目,这里我们主要是来介绍如何写爬虫,至于Objective-C项目自己创建即可。

初始化

博主这里在主控制器上面添加一个UITextView
背景色为黑色;
Tint 颜色为绿色,(这样的颜色看起来比较高大上一点);
不可编辑;
打开超连接功能;
并且连接到所在的控制器。
如图:

创建工具类

创建Spider类和SpiderOption类。
Spider是爬虫工具类;
SpiderOption是参数类,在爬数据过程中所需要的参数都由SpiderOption传递给Spider

SpiderOption.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#import <Foundation/Foundation.h>

typedef void(^SpiderCompleteHander)(NSString* url);

@interface SpiderOption : NSObject

/**
目标网站
*/
@property (nonatomic, copy) NSString* website;

/**
抓取数据正则表达式
*/
@property (nonatomic, copy) NSString* pattern;

/**
Url抓取完成回调
*/
@property (nonatomic, copy) SpiderCompleteHander progress;

@end

Spider.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#import <Foundation/Foundation.h>
#import "SpiderOption.h"

@interface Spider : NSObject

/**
参数类
*/
@property (nonatomic, strong, readonly) SpiderOption* option;

/**
初始化方法

@param option 参数类
@return self
*/
- (instancetype)initWithOption:(SpiderOption*)option;

/**
开始抓取
*/
- (void)start;

@end

Spider.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import "Spider.h"

@implementation Spider

- (instancetype)initWithOption:(SpiderOption *)option{
self = [super init];
if (self) {
_option = option;
}
return self;
}

- (void)start{

}

@end

测试工具类

经过第二步之后,初步的爬虫工具就可以使用了,下面我们在ViewController.m里面调用我们的爬虫工具。

ViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#import "ViewController.h"
#import "Spider.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (strong, nonatomic) Spider *spider;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

//初始化一个参数类
SpiderOption* option = [[SpiderOption alloc] init];

//准备目标网址
option.website = @"https://www.enterdesk.com";

//处理回调,当成功抓取一个内容之后,我们就把他拼接到textView的底部
option.complete = ^(NSString *url) {
[self showLogs:url];
};

//初始化爬虫类
self.spider = [[Spider alloc] initWithOption:option];

//开始抓取
[self.spider start];
}

- (void)showLogs:(NSString*)log{

//把Log内容拼接到textView最下面,并且滚动到最下面
if (log.length > 0) {
[self.textView insertText:[NSString stringWithFormat:@"%@\n\n",log]];
[self.textView scrollRangeToVisible:NSMakeRange(self.textView.text.length, 0)];
}
}

@end

现在来运行一下我们的项目,虽然不会报错,但是并没有开始爬什么数据,接下让我们来完善我们的Spider工具类。

完善工具类

在我们的Spider.m里面添加一个方法,来获取网页的HTML源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)loadUrl:(NSString*)urlString{

WeakSelf;
urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];

NSURL* url = [NSURL URLWithString:urlString];

NSURLSessionConfiguration* configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession* session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDataTask* task = [session dataTaskWithURL:url
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
NSString* html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",html);
}
}];

[task resume];
}

接下来在我们的start方法调用上述方法:

1
2
3
- (void)start{
[self loadUrl:self.option.website];
}

让我们在运行一次项目,如果网络没有问题的话,我们就能看到控制器上面输出的HTML源码,尽管我们的textView还是黑黑的一片。如下图:

正则匹配

我们再次给Spider.m添加一个方法,这个方法主要就是从HTML里面提取出我们想要的内容,比如我们的图片,然后回调给控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)matchesStringInHtml:(NSString*)html{
WeakSelf;

NSRegularExpression* expression = [NSRegularExpression regularExpressionWithPattern:self.option.pattern
options:NSRegularExpressionCaseInsensitive
error:nil];

NSArray<NSTextCheckingResult*>* results = [expression matchesInString:html
options:NSMatchingReportCompletion
range:NSMakeRange(0, html.length)];

NSMutableArray<NSString*>* strings = [NSMutableArray arrayWithCapacity:results.count];

for (NSTextCheckingResult* result in results) {
NSString* sub = [html substringWithRange:result.range];
[strings addObject:sub];
}

weakSelf.option.progress ? weakSelf.option.progress(strings) : nil;

}

方法写好了,我们需要在loadUrlcompletionHandler调用一下.

1
2
3
4
5
6
7
8
9
	
···
NSString* html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (!error && html.length > 0) {
[sSelf fetchImgWithHtml:html];
}else{
NSLog(@"加载出错:%@",error);
}
···

还需要给SpiderOptionpattern赋值,那我们在ViewController.m里面添加以下正则即可:

1
2
3
4
5
6

...
option.website = @"https://www.enterdesk.com";

option.pattern = @"(?<=(src=\"))https?://.+?.(jpg|png)";
...

最后,让我们运行以下看效果吧,是不是很简单呀!!!

备注

我们来解释一下正则(?<=(src=\"))https?://.+?.(jpg|png)表达式的含义:

HTML中的图片地址可能类似于http://aaa.jpg、又或者是以https开头的https://aaa.jpg,那我们就用https?://aaa.jpg即可匹配http也可匹配https?在这里表示前一个字符s有0个或者1个。

或许网页中的图片不仅仅是.jpg又或者是.png.gif等等,我们用或|提取我们想要的格式图片。比如.(jpg|png)就是我们想取jpg或者是png的图片,如果你也想提取.gif的图片,可以这样写.(jpg|png|gif)即可。

我们再说说中间的.+?,.代表单个字符,比如正则表达式这样写http://..jpg我们只能提取http://a.jpg这中间只有单个字符的url。那如果我们想提取中间个数不确定的呢?比如http://abc.jpg?我们就在.后面加个+即可;+表示大于等于1个,比如http://.+.jpg我们既可以匹配到http://a.jpg也可匹配到http://aaa.jpg等中间多个字符的url

那么后面的?呢,这里的?多代表的是非贪婪匹配,举个例子:http://aa.jpg.jpg像这种url我们该怎么提取呢,如果按照刚才http://.+.jpg的表达式匹配出来的是http://aa.jpg.jpg整个串,但是我们只想提取到http://aa.jpg怎么办,我们就在+后面加个?代表尽可能少的匹配,就是所谓的非贪婪匹配。

最后在说一下(?<=(src=\"))这个意思就是我们要提取src="后面的http:xx.jpg的图片,但是不包含src="。举个🌰:如果把这个表达式去掉,我们会发现提取到的url有可能是这样https://www.enterdesk.com/bizhi/5488.html\"><img src=\"https://up.enterdesk.com/edpic_360_360/34/3f/f4/343ff4ada1548da0a81388f86dd9d1af.jpg,很显然这不是我们想要的结果。所以说这个就是起到了相应的作用。

相关链接:

项目源码

用Objective-C写一个简单的爬虫(二)

------ 本文结束------
0%