標籤:
網路爬蟲-使用Regex抓取網路資料
關於網路資料抓取不僅僅在iOS開發中有,其他開發中也有,也叫網路爬蟲,大致分為兩種方式實現
- 1:正則表達
- 2:利用其他語言的工具包:java/Python
先來看看網路爬蟲的基本原理:
一個通用的網路爬蟲的架構:
網路爬蟲的基本工作流程如下:
1.首先選取一部分精心挑選的種子URL;
2.將這些URL放入待抓取URL隊列;
3.從待抓取URL隊列中取出待抓取在URL,解析DNS,並且得到主機的ip,並將URL對應的網頁下載下來,儲存進已下載網頁庫中。此外,將這些URL放進已抓取URL隊列。
4.分析已抓取URL隊列中的URL,分析其中的其他URL,並且將URL放入待抓取URL隊列,從而進入下一個迴圈。
以下內容均為本人個人理解。
網路資料抓取
- 概念:網路資料抓取,也叫網路爬蟲。是在我們iOS程式中,擷取要抓取到的網頁上的資料。
- 用處:如果要用到某網站的一些資料,這個時候我們就要用到抓取資料技術。
- 建議:建議抓取過程中,多利用分類,多寫一些分類方法,有助於提高程式可讀性,也可提高效率。
今天先來介紹一下第一種:Regex
注意點:
其實網路抓取資料很簡單,但是有用到Regex,這個有人說難,有人說很難,有人說非常難,其實我們抓取資料只會用到“." 、"*"、"?"這三個符號!
Regex中:“.”是包括任何字元不包括分行符號,“*”是任意多個的字元,“?”是指到最近的一個URL,如果沒有就是到最遠的一個!
1 NSString *pantten = [NSString stringWithFormat:@"<ul class=\"cs_list\">(.*?)</ul>"];2 3 NSRegularExpression *regx = [NSRegularExpression regularExpressionWithPattern:pantten options:NSRegularExpressionCaseInsensitive |NSRegularExpressionDotMatchesLineSeparators error:NULL];
其中有兩個參數需要大家瞭解一下,很重要
| 12345 |
NSRegularExpressionCaseinsensitive 不區分大小寫 NSRegularExpressionDotMatcheLineSeparators 讓“點”字元可以匹配分行符號 |
抓資料,其實主要會寫匹配字串就行
- (.*?)表示要抓到的內容
- .*?表示要忽略的內容,愛是啥是啥
- 字串轉義雙引號用\轉義括弧用\\
在開發項目的過程,很多情況下我們需要利用互連網上的一些資料,在這種情況下,我們可能要寫一個爬蟲來爬我們所需要的資料。一般情況下都是利用Regex來匹配Html,擷取我們所需要的資料。一般情況下分以下三步。
1、擷取網頁的html
- 2、利用Regex,擷取我們所需要的資料
- 3、分析,使用擷取到的資料,(例如,儲存到資料庫)
接下來我們分析代碼:
1、擷取網頁的html
對於一些網頁,不需要提交Post提交資料時,我們可以簡單的利用NSURL類來擷取我們所需要的html,交將其轉換中kCFStringEncodingGB_18030_2000格式,解決中文亂碼問題。
1 +(NSString*) urlstring:(NSString*)strurl{ 2 NSURL *url = [NSURL URLWithString:strurl]; 3 NSData *data = [NSData dataWithContentsOfURL:url]; 4 5 NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); 6 NSString *retStr = [[NSString alloc] initWithData:data encoding:enc]; 7 8 //NSLog(@" html = %@",retStr); 9 10 return retStr;11 }
對於需要Post提交資料的網頁,我們可以利用強大的ASIFormDataRequest類來實現,例如:
1 +(void)getPostResult:(NSString*)startqi{ 2 ASIFormDataRequest *request = [[ASIFormDataRequest alloc] initWithURL:[NSURL URLWithString:URLPost]]; 3 4 [request setPostValue:startqi forKey:@"startqi"]; 5 [request setPostValue:@"20990101001" forKey:@"endqi"]; 6 [request setPostValue:@"qihao" forKey:@"searchType"];//網頁的中的搜尋方式 7 [request startSynchronous]; 8 9 NSData* data = [request responseData];10 11 if (data==nil) {12 FCLOG(@"has not data");13 }14 else{15 NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); 16 NSString *retStr = [[NSString alloc] initWithData:data encoding:enc];17 FCLOG(@"html = %@",retStr); 18 }19 }
這樣的話,我們就通過了兩種方式擷取了我們所需要的html
2、分析html
關於利用Regex匹配問題,我又對NSString類擴充了一個方法-(NSMutableArray *)substringByRegular:(NSString *)regular。根據傳入的Regex,返回所有匹配的數組。
1 @implementation NSString(StringRegular) 2 3 4 -(NSMutableArray *)substringByRegular:(NSString *)regular{ 5 6 NSString * reg=regular; 7 8 NSRange r= [self rangeOfString:reg options:NSRegularExpressionSearch]; 9 10 NSMutableArray *arr=[NSMutableArray array];11 12 if (r.length != NSNotFound &&r.length != 0) {13 14 int i=0;15 16 while (r.length != NSNotFound &&r.length != 0) {17 18 FCLOG(@"index = %i regIndex = %d loc = %d",(++i),r.length,r.location);19 20 NSString* substr = [self substringWithRange:r];21 22 FCLOG(@"substr = %@",substr);23 24 [arr addObject:substr];25 26 NSRange startr=NSMakeRange(r.location+r.length, [self length]-r.location-r.length);27 28 r=[self rangeOfString:reg options:NSRegularExpressionSearch range:startr];29 }30 }31 return arr;32 }33 @end
在這種情況下,我們首先我得到我們要擷取資料的Regex,關於Regex這種火星文我就不多說了,我也很糾結,我就不多說了,但是有一點就是,所寫的Regex一定是我們所需要的資料,並且能夠屏蔽無效資訊的,有可能在一次匹配中無法擷取,可以多次利用Regex來分段擷取。下面是我的語句,在我的例子中,就是兩次利用Regex。
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等這些資訊我是不需要的,所以再次利用Regex來分析NSString
1 if (arr!=nil&&[arr count]>0) { 2 3 NSString *[email protected]"\\w{11}"; 4 NSString *[email protected]"(\\w{2}\\s{0,1}){8}"; 5 6 TicketResultService *service=[[TicketResultService alloc] init]; 7 [[Sqlite3Helper Instance] openDB]; 8 for (NSString *sub in arr) { 9 10 TicketResult* r=[[[TicketResult alloc] init] autorelease];11 12 NSMutableArray* prearr=[sub substringByRegular:prereg];13 14 if (prearr!=nil&&[prearr count]>0) {15 r.sectionID=(NSString*)[prearr objectAtIndex:0];16 }17 else{18 continue;19 }20 21 NSMutableArray *backarr=[sub substringByRegular:backreg];22 if (backarr!=nil&&[backarr count]>0) {23 r.result=[backarr objectAtIndex:0];24 }25 else{26 continue;27 }28 29 if([service isExist:r.sectionID]){30 continue;31 }32 33 r.type=[NSNumber numberWithInt:1];34 35 [service addModel:r];36 37 }38 [[Sqlite3Helper Instance] closeDB];39 40 [service release];41 }
以上爬蟲才算正式完成,其實,在此之前還有一個第0步,即判斷裝置目前的網路狀態,如果沒有連網的就沒有必要去爬蟲了,因為你也爬不到任何的資料。判斷網路狀態我是利用Apple官方的一個例子Reachability,網上也有很多關於這個的例子,我就不再細說了,非常感謝網上的各位大牛們提供的很好的辦法,讓我能更快的寫出這些。
iOS開發——網路使用技術OC篇&網路爬蟲-使用Regex抓取網路資料