Phân tích cú pháp html thành văn bản

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 sử dụng

Ở đây vấn đề phân tích cú pháp 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 ứng dụng 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 tưởng hơi điên rồ 1 chút là có thể tạo ra ứng dụng ngay. Các bạn cứ tưởng tượng đi nhé còn mình thì hãy vào bài viết hướng dẫn nhé =))

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 họa cho 1 trang web đơn giản.

Phân tích cú pháp html thành văn bản
Để nhìn rõ 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 thẻ trong một trang web.
Phân tích cú pháp html thành văn bản

Như trên, khi bạn muốn lấy văn bản nội dung "một số trang web", bạn phải đi từ nút html -> đầu -> tiêu đề, và dòng văn bản đó chính là nội dung của tiêu đề thẻ. Nếu theo xpath bạn sẽ đc. /html/đầu/tiêu đề

Còn khi muốn lấy "đây là đoạn thứ hai" bạn sẽ phải đi theo dòng sau. /html/body/p[class='special']

Có thể 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 dự án rồi làm theo sẽ hiểu

Đầu tiên chúng ta tạo dự án có tên phân tích cú pháp html

Phân tích cú pháp html thành văn bản

Tiếp theo chúng ta tạo ra 1 mô hình để 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

Phân tích cú pháp html thành văn bản

#import <Foundation/Foundation.h>

@interface DataItem : NSObject
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *url;
@end

Tiếp tục tạo lớp 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ẽ thêm các thư viện trên github với liên kết sau

https://github.com/topfunky/hpple

Bạn giải nén và sao chép các tệp sau.

Phân tích cú pháp html thành văn bản
Bạn nhớ tạo 1 thư mục hpple và thả các tệp vừa chọn phía trên vào nhé. Nhớ chọn copy item và target nữa nhé.
Phân tích cú pháp html thành văn bản
Tiếp tục theo chúng ta thêm thư viện libxml2
Phân tích cú pháp html thành văn bản
Giờ chúng ta sẽ lấy html từ liên kết sau. http. //vovgiaothong. vn/luat-giao-thong/9 Trước tiên chúng ta cần phải lấy nguồn của trang web đã về. cái hướng dẫn 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ẽ sử dụng cái thư viện hpple để phân tích cái dữ liệu, sau đó chúng 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ứ dưới dạng xpath thì dấu "//" tức là search mọi nơi cho đến khj cuối cây thì thôi. ở trên chuỗi truy vấn của mình, nó sẽ tìm ra các thẻ div có lớp tên là tiêu đề và lấy ra thẻ div phía trong. Có thể 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 tưởng còn trẻ mình làm đoạn này cũng thắc mắc lắm. Bạn có thể tham khảo thêm phần mã trên reywenderlich sau.

Phân tích cú pháp html thành văn bản

Giờ quay lại trang web mà mình đã đưa lên trên. Chúng ta sẽ lấy tiêu đề và link + ảnh của bài viết. Các 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 gỡ lỗi cụ thể từng phần của nó được trả về 1 để có thể lấy được các phần như ý muốn. Vì dữ liệu trả về bao gồm cả các phần mình không cần thiết. Và với dữ liệu 1 trang web 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 không hướng dẫn cụ thể các phần hiển thị ra nữa vì nó rất đơn giản không cần thiết phải đư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 window tay giao thông của mình