在开发项目的过程,很多情况下我们需要利用互联网上的一些数据,在这种情况下,我们可能要写一个爬虫来爬我们所需要的数据。一般情况下都是利用正则表达式来匹配Html,获取我们所需要的数据。一般情况下分以下三步。
1、获取网页的html
2、利用正则表达式,获取我们所需要的数据
3、分析,使用获取到的数据,(例如,保存到数据库)
2、分析html
关于利用正则表达式匹配问题,我又对NSString类扩展了一个方法-(NSMutableArray *)substringByRegular:(NSString *)regular。根据传入的正则表达式,返回所有匹配的数组。
其实在这个arr数组中一条就是对应我数据库表中的一条记录,但是像td class等这些信息我是不需要的,所以再次利用正则表达式来分析NSString
1、获取网页的html
2、利用正则表达式,获取我们所需要的数据
3、分析,使用获取到的数据,(例如,保存到数据库)
接下来我们分析代码:
1、获取网页的html
对于一些网页,不需要提交Post提交数据时,我们可以简单的利用NSURL类来获取我们所需要的html,交将其转换中kCFStringEncodingGB_18030_2000格式,解决中文乱码问题。
+(NSString*) urlstring:(NSString*)strurl{
NSURL *url = [NSURL URLWithString:strurl];
NSData *data = [NSData dataWithContentsOfURL:url];
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *retStr = [[NSString alloc] initWithData:data encoding:enc];
//NSLog(@" html = %@",retStr);
return retStr;
}
对于需要Post提交数据的网页,我们可以利用强大的ASIFormDataRequest类来实现,例如:
+(void)getPostResult:(NSString*)startqi{
ASIFormDataRequest *request = [[ASIFormDataRequest alloc] initWithURL:[NSURL URLWithString:URLPost]];
[request setPostValue:startqi forKey:@"startqi"];
[request setPostValue:@"20990101001" forKey:@"endqi"];
[request setPostValue:@"qihao" forKey:@"searchType"];//网页的中的搜索方式
[request startSynchronous];
NSData* data = [request responseData];
if (data==nil) {
FCLOG(@"has not data");
}
else{
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *retStr = [[NSString alloc] initWithData:data encoding:enc];
FCLOG(@"html = %@",retStr);
}
}
这样的话,我们就通过了两种方式获取了我们所需要的html
2、分析html
关于利用正则表达式匹配问题,我又对NSString类扩展了一个方法-(NSMutableArray *)substringByRegular:(NSString *)regular。根据传入的正则表达式,返回所有匹配的数组。
@implementation NSString(StringRegular)
-(NSMutableArray *)substringByRegular:(NSString *)regular{
NSString * reg=regular;
NSRange r= [self rangeOfString:reg options:NSRegularExpressionSearch];
NSMutableArray *arr=[NSMutableArray array];
if (r.length != NSNotFound &&r.length != 0) {
int i=0;
while (r.length != NSNotFound &&r.length != 0) {
FCLOG(@"index = %i regIndex = %d loc = %d",(++i),r.length,r.location);
NSString* substr = [self substringWithRange:r];
FCLOG(@"substr = %@",substr);
[arr addObject:substr];
NSRange startr=NSMakeRange(r.location+r.length, [self length]-r.location-r.length);
r=[self rangeOfString:reg options:NSRegularExpressionSearch range:startr];
}
}
return arr;
}
@end
在这种情况下,我们首先我得到我们要获取数据的正则表达式,关于正则表达式这种火星文我就不多说了,我也很纠结,我就不多说了,但是有一点就是,所写的正则表达式一定是我们所需要的数据,并且能够屏蔽无效信息的,有可能在一次匹配中无法获取,可以多次利用正则表达式来分段获取。下面是我的语句,在我的例子中,就是两次利用正则表达式。
NSString *regstr = @"<td class=\'z_bg_05\'>\\w{11}</td><td class=\'z_bg_13\'>(\\w{2}\\s{0,1})*</td>";
NSMutableArray *arr=[strhtml substringByRegular:regstr];
3、分析或利用数据,在这里,我只是利用上一篇博客上所述方法简单的把这些数据保存到了数据库(sqlite3)中。
其实在这个arr数组中一条就是对应我数据库表中的一条记录,但是像td class等这些信息我是不需要的,所以再次利用正则表达式来分析NSString
if (arr!=nil&&[arr count]>0) {
NSString *prereg=@"\\w{11}";
NSString *backreg=@"(\\w{2}\\s{0,1}){8}";
TicketResultService *service=[[TicketResultService alloc] init];
[[Sqlite3Helper Instance] openDB];
for (NSString *sub in arr) {
TicketResult* r=[[[TicketResult alloc] init] autorelease];
NSMutableArray* prearr=[sub substringByRegular:prereg];
if (prearr!=nil&&[prearr count]>0) {
r.sectionID=(NSString*)[prearr objectAtIndex:0];
}
else{
continue;
}
NSMutableArray *backarr=[sub substringByRegular:backreg];
if (backarr!=nil&&[backarr count]>0) {
r.result=[backarr objectAtIndex:0];
}
else{
continue;
}
if([service isExist:r.sectionID]){
continue;
}
r.type=[NSNumber numberWithInt:1];
[service addModel:r];
}
[[Sqlite3Helper Instance] closeDB];
[service release];
}
以上爬虫才算正式完成,其实,在此之前还有一个第0步,即判断设备目前的网络状态,如果没有联网的就没有必要去爬虫了,因为你也爬不到任何的数据。判断网络状态我是利用Apple官方的一个例子Reachability,网上也有很多关于这个的例子,我就不再细说了,非常感谢网上的各位大牛们提供的很好的办法,让我能更快的写出这些。