Parse HTML on IOS
Cuối tháng rồi mà chưa có đề tài báo cáo, tiện đang làm dự án nho nhỏ về parse html nên dịch từ cái demo trên mạng cho ae nào cần dùng. Ở đây vấn đề parse html không phải là vấn đề khó nhưng ít ai động đến, nhưng đây cũng là 1 ý tưởng 1 app của mình đã lên store. Bạn biết đấy không cần làm gì cao ...
Cuối tháng rồi mà chưa có đề tài báo cáo, tiện đang làm dự án nho nhỏ về parse html nên dịch từ cái demo trên mạng cho ae nào cần dùng.
Ở đây vấn đề parse html không phải là vấn đề khó nhưng ít ai động đến, nhưng đây cũng là 1 ý tưởng 1 app của mình đã lên store. Bạn biết đấy không cần làm gì cao siêu mà chỉ cần có những ý tưởng hơi điên rồ 1 chút là có thể tạo ra app ngay. Bạn hãy cứ tưởng tượng đi nhé còn mình sẽ đi vào bài viết hướng dẫn =)).
Như các bạn đã biết, một trang web là tập hợp của các thẻ html đc định nghĩa sẵn và webview chỉ hiển thị nội dung. Dưới đây sẽ là hình ảnh đơn giản nhất minh hoạ cho 1 trang web đơn giản. Để nhìn rõ ràng hơn các bạn có thể nhìn hình ảnh sau, nó sẽ minh hoạ cho chúng ta về phân cấp các tag trong một website.
Như hình trên, khi bạn muốn lấy nội dung text "some webpage", bạn phải đi từ node html ->head -> title, và dòng text đó chính là nội dung của thẻ title. Nếu theo xpath bạn sẽ đc: /html/head/title.
Còn khi muốn lấy "this is second paragraph" bạn sẽ phải đi theo dòng sau: /html/body/p[class='specical'].
Có lẽ hơi khó hình dung chúng ta sẽ phải làm gì tiếp theo nên chúng ta cứ tạo project rồi làm theo sẽ hiểu.
Đầu tiên chúng ta tạo project có tên parse html
Tiếp theo chúng ta tạo ra 1 model để chứa các thông tin cần lấy. Chúng ta tạo 1 class có tên DataItem kế thừa từ NSObject
#import <Foundation/Foundation.h> @interface DataItem : NSObject @property (strong, nonatomic) NSString *name; @property (strong, nonatomic) NSString *url; @end
Tiếp tục tạo class có tên Contributor.
@interface Contributor : NSObject @property (strong, nonatomic) NSString *name; @property (strong, nonatomic) NSString *imageUrl; @end
Việc nữa là bạn sẽ add các thư viện trên github với link sau:
https://github.com/topfunky/hpple
Bạn giải nén và cop những file sau: Bạn nhớ tạo 1 thư mục hpple và thả các file vừa select phía trên vào nhé. Nhớ chọn copy item và target nữa nhé. Tiếp theo chúng ta thêm thư viện libxml2 Giờ chúng ta sẽ lấy html từ link sau: http://vovgiaothong.vn/luat-giao-thong/9 Trước tiên chúng ta cần phải lấy source của trang web về đã. cái tutorial của reywenderlich nó ko nói rõ cái này nhưng mình sẽ đưa ra hàm lấy source cho các bợn trẻ dễ hiểu.
url=[NSURL URLWithString:[NSString stringWithFormat:@"http://vovgiaothong.vn/luat-giao-thong/9"]]; data=[NSData dataWithContentsOfURL:url]; NSString *htmlbody=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",htmlbody);
Tiếp theo chúng ta sẽ dùng cái thư viện hpple để parse cái data, sau đó ta sẽ truy xuất kiểu xpath.
NSURL *url=[NSURL URLWithString:link]; NSData *data=[NSData dataWithContentsOfURL:url]; NSString *htmlbody=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; TFHpple *hppleParser=[TFHpple hppleWithHTMLData:data]; NSString *query=@"//div[@class='title']/div";
Các bạn lưu ý, truy xuất dứoi dạng xpath thì dấu "//" tức là search mọi nơi cho đến khj cuối tree thì thôi. ở trên chuỗi query của mình nó sẽ tìm ra những tag div có class tên là title và lấy ra thẻ div phía trong. Có lẽ các bạn trẻ đã hiểu sơ qua về cách truy xuất trong xpath rồi, nếu chưa hiểu các bạn cứ luyện thêm đi hồi còn trẻ mình làm đoạn này cũng mắc lắm. Bạn có thể tham khảo thêm phần code trên reywenderlich sau:
Giờ quay trở lại trang web mà mình đã đưa ở trên. Chúng ta sẽ lấy ra các title và link + ảnh của bài viết. Bạn tham khảo phần code dưới đây của mình:
- (void)getData { //1 NSURL *url=nil; NSData *data=nil; NSString *titleNews=nil; NSString *imgTitle=nil; NSString *link=nil; NSArray *arrayTitle = nil; NSArray *arrayImage = nil; NSArray *arrayDesc = nil; // for (int i=10; i<=100; i+=10) { url=[NSURL URLWithString:[NSString stringWithFormat:@"http://hk.on.cc/hk/news/"]]; data=[NSData dataWithContentsOfURL:url]; NSString *htmlbody=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",htmlbody); //2 TFHpple *hppleParser=[TFHpple hppleWithHTMLData:data]; //3 // get hot new NSString *query=@"//div[@class='carousel-inner']/div[@class=' item']/div/h2/a"; arrayTitle = [hppleParser searchWithXPathQuery:query]; query=@"//div[@class='carousel-inner']/div[@class=' item']/div/a/img"; arrayImage = [hppleParser searchWithXPathQuery:query]; query = @"//div[@class='carousel-inner']/div[@class=' item']/div[2]"; arrayDesc = [hppleParser searchWithXPathQuery:query]; for (int i = 0; i < arrayImage.count; i++) { titleNews = [[[[arrayTitle objectAtIndex:i] children] objectAtIndex:0] content]; link = [[arrayTitle objectAtIndex:i] objectForKey:@"href"]; imgTitle = [NSString stringWithFormat:@"http://vovgiaothong.vn%@",[[arrayImage objectAtIndex:i] objectForKey:@"src"]]; [self getDetailsNews:titleNews imageUrl:imgTitle link:[NSString stringWithFormat:@"http://vovgiaothong.vn%@",link]]; } query = @"//div[@class='row dot']/div[@class='col-xs-16 col-md-16']/div/div/a"; arrayImage = [hppleParser searchWithXPathQuery:query]; query = @"//div[@class='row dot']/div[@class='col-xs-19 col-md-19']/div/h3/a"; arrayTitle = [hppleParser searchWithXPathQuery:query]; for (int i = 0; i < arrayImage.count; i++) { imgTitle = [[[arrayImage objectAtIndex:i] firstChild] objectForKey:@"src"]; if ([imgTitle rangeOfString:@"http://"].location == NSNotFound) { imgTitle = [NSString stringWithFormat:@"http://vovgiaothong.vn%@",imgTitle]; } titleNews = [[[arrayTitle objectAtIndex:i] firstChild] content]; link = [NSString stringWithFormat:@"http://vovgiaothong.vn%@",[[arrayTitle objectAtIndex:i] objectForKey:@"href"]]; [self getDetailsNews:titleNews imageUrl:imgTitle link:link]; } [process hide:YES]; }
Về việc lấy các thẻ html này sẽ gặp phải rất nhiều vấn đề, bạn cần phải debug cụ thể từng phần nó trả ra 1 để có thể lấy được các phần như ý muốn. Vì data trả về gồm cả các phần mình ko cần thiết. Và với 1 trang web data trả về là động nên chúng ta chỉ có thể dựa vào các phần tĩnh để lấy. Ở đây mình ko hướng dẫn cụ thể phần hiển thị ra nữa vì nó rất là đơn giản chẳng cần thiết đưa ra ở đây. Ở dưới mình sẽ cho các bạn 1 link demo cụ thể là project lấy tin tức từ VOV của mình phục vụ cho việc lấy dữ liệu cho app sổ tay giao thông của mình.