読者です 読者をやめる 読者になる 読者になる

UIWebViewでスクレイピングするライブラリの試作版を作った

これ。

サンプルアプリでPinterestのpopular(http://m.pinterest.com/popular/)の写真を閲覧するやつを入れた。

想定する使い方はこんな感じ。取得したい要素のXPathを指定する。

Example/RootViewController.m#L34

  // UIViewController を継承したクラス
  [self.webScraper scrape:@"http://m.pinterest.com/popular/" 
                 selector:@"//div[@id=\"wrapper\"]//div[@class=\"image\"]//a/img"
                  handler:^(NSArray *elements, NSError *error) {
                    NSLog(@"Load %d elements.", elements.count);
                    
                    for (NSDictionary* img in elements) {
                      NSString* url = [[img objectForKey:@"attributes"] objectForKey:@"src"];
                      [self.imageURLs addObject:url]; // NSMutableArray* imageURLs;
                    }
                  }];

UIViewControllerのカテゴリとして、プロパティ経由で呼び出して使う(なんか気持ち悪いが今のところこの方法しかうまくいってない)。ほんとはviewに依存しないHeadlessな感じにしたいけど、できてない。
仕組み的には1x0のサイズのUIWebViewインスタンスを呼び出し元のUIViewControllerのviewに追加して内部でリクエストを処理してる(昭和のモジュールっぽい)。
ページ読み込みが完了したらJavaScriptでdocument.head, document.body のinnertHTMLをhppleっていうパーサーで解析して結果を返している。
CSSセレクタとかサポートできそうだし、JavaScriptベースで要素選択まですればいいじゃんと最初は思ってけど重くなりそうな予感や、デバッグ地獄そうなので回避した。あと途中までUser-Agentの切り替えもサポートしてたんだけどこれもうまくいかなくなって一旦パスした。

構想としてはこんな感じ
・単純なHTMLリソースのスクレイピングはHTTPクライアントでできるので専門外
・またはサーバーサイドのバッチ処理スクレイピングして全ユーザー共通にキャッシュ効いているやつをWeb API経由で配信するとかが通常とられている
・なのでクライアントサイドでJavaScriptを評価した後のHTMLから情報を取得したいケースに使う
・ex:Google検索の結果をUITableViewに表示とか……
・ex:ユーザーログイン後のマイページ内の情報……とか
・開発者向け公開Web APIがないサイトとか(外部サイトの使用変更につられるので、動作保証はあんまりできなげな方法)
・PhantomJSみたいな役割にしたい
・自動テストの補助とか

PerlWeb::Scraperを想起される名前になっているけど、最初思いついた段階でそれっぽい路線で作っていたけど、どんどん関係なくなりそうなので名前は変えるかもしれない。