iOS 抓取 HTML ,CSS XPath 解析數據

以前我們獲取數據的方式都是使用 AFN 來 Get JSON 數據,比如 點我查看 JSON 數據.

但例如下面的百度貼吧,和豆瓣讀書等網站..并不提供我們獲取數據的 API

百度貼吧:

百度貼吧數據.png

豆瓣讀書:

豆瓣讀書數據.png

這時我們可以解析他們的 HTML 來獲取我們想要的數據.

工具準備

這時我們需要2個工具,FirefoxFireBug.

你可以在 這里下載 FireFox 瀏覽器,然后在其右上角菜單的附加組件管理器中下載 FireBug 插件.
FireBug 有很強大的 JavaScript 調試功能,還能實時編輯 HTML CSS,是前端同學喜愛的一個工具.
下載安裝好以后 點擊右上角的 Bug(蟲子)圖標來使用 FireBug 調試當前網頁.

如果你不了解 XPath ,可以學習 w3school 的教程.

打開 FireBug.png

Ono 開源庫

Ono 是一個 Github 上的開源項目,它能方便我們解析 XML,HTML 標簽,并且支持 CSS XPath 搜索特定節點.

你可能沒聽過這個庫,但其作者你肯定知道. Mattt Thompson,它是 AFN 的作者,還是博客 NSHipster 的作者.

Swift 版本類似的開源庫 Ji
Java 或 Android 可以使用 Jsoup

開始

準備工作都 Ok 了..我們開始編碼.新建一個空白工程,注意,如果要在 Info.plist 中添加兩行 App Transport Security Settings,和 Allow Arbitrary Loads YES, 來允許 HTTP 傳輸.

App 允許 Http.png

然后使用 CocoaPods 添加第三方庫 pod 'Ono'.

這里,要解析的 HTMl 數據就用我的博客了

再創建一個 Post 類繼承自 NSObject,代表每一篇文章 ,修改 .h 文件如下

#import <Foundation/Foundation.h>
@class ONOXMLElement;

@interface Post : NSObject
@property (copy,nonatomic) NSString *title; //文章標題
@property (copy,nonatomic) NSString *postDate; //文章發表時間
@property (copy,nonatomic) NSString *postUrl; //文章正文內容的 Url

+(NSArray*)getNewPosts; //獲取所有文章
+(instancetype)postWithHtmlStr:(ONOXMLElement*)element; //用 HTMl 數據創建 Post 類
@end

.m 文件中導入 Ono,并添加一個常量 Url.

#import <Ono.h> 

static NSString *const kUrlStr=@"http://BigPi.me";

然后我們可以用 AFN 等下載該 Url 的 HTML數據,再使用 XPath 獲取代表每一篇文章的 XPath,

先打開 FireFox 和 FireBug ,點擊下面的圖標

FireBug 元素選擇器.png

在適當移動鼠標,點擊選擇網頁上的一篇文章,

Post數據.png

這時我們可以看到,下面 FireBug 的 HTMl 樹展開了,我們可以發現,每一個 <div class="post">標簽都包含一篇文章的數據.
我們右鍵 <div id="posts"> ,復制其 XPath

復制 XPath.png

復制出來的結果 //*[@id="posts"],這個 <div id="posts">節點下面的每一個子節點都代表一篇文章,
我們現在來使用這個 XPath 獲取所有的 HTML 數據.

Post.m 添加如下方法:

+(NSArray*)getNewPosts{
    NSMutableArray *array=[NSMutableArray array];
    NSData *data= [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrlStr]]; //下載網頁數據
    
    NSError *error;
    ONOXMLDocument *doc=[ONOXMLDocument HTMLDocumentWithData:data error:&error]; 
    ONOXMLElement *postsParentElement= [doc firstChildWithXPath:@"http://*[@id='posts']"]; //尋找該 XPath 代表的 HTML 節點,
    //遍歷其子節點,
    [postsParentElement.children enumerateObjectsUsingBlock:^(ONOXMLElement *element, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"%@",element);
    }];
    return array;
}

并在ViewController.m 中調用這個方法:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [Post getNewPosts];
}

@end

運行后查看 Console, 我們已經可以獲取到每篇文章的 HTMl 了,然后我們再來解析每篇文章的具體數據.

切換到 FireBug,展開其中一篇文章的節點,

文章 HTML 節點.png

我們可以看到<h2 class="title">節點下的

  • <a href="/post/jazzhands/jazzhands-yuan-ma-shi-xian-fen-xi">
  • <i class="fa fa-leaf"></i>
  • JazzHands 源碼實現分析</a>

標簽中,有文章的具體Url, 和文章標題,

<div class="info">節點下的,

  • <span class="date">
  • <i class="fa fa-clock-o"></i>
  • 2016-03-04 21:39</span>

標簽有文章發布的時間,此時我們可以右鍵點擊節點,復制文章標題,發布時間等節點的 XPath,
但這里我們使用相對的 XPath.
每篇文章的 HTML 結構如下:

  • <div class="post">
  • 文章標題 Url等內容
  • </div>

所以我們的

  • 文章 Url XPath : "h2/a"
  • 文章標題 XPath : a 標簽的 href 屬性值
  • 文章發布時間 XPath : "div[2]/span[1]"

接下來我們來解析每一篇文章的詳細數據

Post.m 中添加方法:

+(instancetype)postWithHtmlStr:(ONOXMLElement*)element{
    
    Post *p=[Post new];
    ONOXMLElement *titleElement= [element firstChildWithXPath:@"h2/a"]; // 根據 XPath 獲取含有文章標題的 a 標簽
    p.postUrl= [titleElement valueForAttribute:@"href"]; //獲取 a 標簽的  href 屬性
    p.title= [titleElement stringValue];
    ONOXMLElement *dateElement= [element firstChildWithXPath:@"div[2]/span[1]"]; //根據 XPath 獲取文章發布時間 span 標簽
    p.postDate= [dateElement stringValue];
    return p;
}

然后修改 +(NSArray*)getNewPosts方法:如下

...
[postsParentElement.children enumerateObjectsUsingBlock:^(ONOXMLElement *element, NSUInteger idx, BOOL * _Nonnull stop) {
        //NSLog(@"%@",element);
        Post *post=[Post postWithHtmlStr:element];
        if(post){
            [array addObject:post];
        }
    }];
...

最后因為我們我們獲取到的 HTMl 的文章 Url 是相對 Url, 類似
/post/jazzhands/jazzhands-yuan-ma-shi-xian-fen-xi
所以我們在 Setter 方法中拼接域名 , http://BigPi.me

-(void)setPostUrl:(NSString *)postUrl{
    _postUrl=[kUrlStr stringByAppendingString:postUrl];
}

我們在下圖位置打斷點查看結果:

代碼斷點.png

運行起來,結果如下:

抓取文章數據結果.png

至此我們已經能使用 FireBug + Ono + XPath 來解析 HTML 數據

我就使用這個辦法獲取我們學校教務管理系統 HTML,制作了一個統計成績,計算績點的 App.

補充

  • FireBug 是一個很強大前端調試工具.

  • 還可以使用 正則表達式 來解析 HTML 數據.不過從 StackOverflow 討論 來看 ,并推薦使用正則來解析 HTML 數據.

  • RayWonderLich 有一篇比較老的教程 ,使用類似的技術解析 HTML

  • 最最后,很重要的一點,HTML 數據可能經常會變動,尤其那個網頁還不是我們自己能管理的網頁,所以 XPath 隨時可能解析失敗,
    如果你一定要使用 XPath 來解析 HTML 數據,可以在服務端進行這個操作,然后修改成 API 的形式,讓手機端像以前一樣 GET JSON 數據.
    同時,服務端還可以設置異常處理,緩存等策略.

本文 Demo 可以在 GitHub 中獲取

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容