で、今回はHTML文の解析にNSXMLParser使ってみます。
 その(143)ではlibxml2を組み込んで、Cのコールバック関数内で自分のクラスのメソッド呼ぶdelegateパターンを自前で用意して対応したんだけど、NSXMLParserの場合は当然最初からdelegateパターンが用意されとります。
 delegateパターンわからん人はモーダルビューを表示する(2)あたりを読んでくれい。

 用意するメソッドは以下のとおり
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
}

- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
}

- (void)parser:(NSXMLParser *)parser
foundCharacters:(NSString *)string {
}

- (void)parser:(NSXMLParser *)parser
parseErrorOccurred:(NSError *)parseError {
}

 ま、呼び出しのタイミング自体(新しいタグの開始、タグの終了、その間の文字列、異常事態)はその(144)の自作delegate版とほぼ同じなわけですが、引数がNSObjectやNSDictionaryとかになっててObjective-Cとして使い勝手がよくなります。
 そのぶん、よぶんな作業で時間を食うわけですが…
 それは、まあ個人の好き好きでいいんじゃないかと思われ。

 あくまでlibxml2でライトな生活を好むもよし、NSXMLParserでお気楽生活を楽しむもよし、NSXMLDocument使ってiPhoneのメモリをヒイヒイ、ラメェ~、それ以上読み込むと壊れちゃうよ~とか言わせてみたりするもよしちゅ~ところですわ。

 libxml2使う場合と違ってlibxml2.dylibを組み込まなくても使えるところもいいですな。

 使い方も一番簡単なやり方だと上のdelegateメソッド用意して
NSXMLParser* parser = [[NSXMLParser alloc] initWithContentsOfURL:
[NSURL URLWithString:@"http://localhost/konohana/mainlist.php"]];
parser.delegate = self;
[parser parse];

 でおしまい。
 非常に簡単。

 ただし、ユーザーの使い勝手を考えると、大きなデータの場合はやっぱり非同期でデータを取り出してからNSXMLParserを使うのが正解。
 その場合はNSURLConnectionのconnectionWithRequestなんかを使って非同期で取り出したNSData* dataを
NSXMLParser* parser = [[NSXMLParser alloc] initWithData:data];

 て感じで渡してやればいい。
 NSOparation内でinitWithContentsOfURL側を使う手も有りか?

 ま、そこらへんは置いといて、とりあえずHTML文の解析。
 まずはdidStartElement:メソッドでattributeDictに何が入ってんだろと
printf("%s\n", [elementName UTF8String]);
NSLog(@"%@", attributeDict);

 ってやってみたコンソール出力結果がこれ。コンソールの出し方わからん人はコンソールを表示するね。
  NSLogに与える最初のパラメータはprintf同様に%sや%dの指定ができる、その中で特殊なのが
  %@
 で、この場合、インスタンスの説明を表示する指定となる。
 正確に言うとインスタンスのdescriptionメソッドを呼び出し、返されたNSString文字列を表示している。descriptionはNSObjectに用意されているメソッドでAppStoreにアップする(2)で説明したようにクラスごとに自由にカスタマイズされている。attributeDictはNSDictionaryなのでキーと対応する内容の表示となるわけですな。

html
2010-06-09 22:15:48.685 htmlParser[3415:207] {
lang = ja;
"xml:lang" = ja;
xmlns = "http://www.w3.org/1999/xhtml";
}
head
2010-06-09 22:15:48.686 htmlParser[3415:207] {
}
meta
2010-06-09 22:15:48.686 htmlParser[3415:207] {
content = "text/html; charset=utf-8";
"http-equiv" = "content-type";
}
link
2010-06-09 22:15:48.686 htmlParser[3415:207] {
href = "style.css";
rel = stylesheet;
type = "text/css";
}
title
2010-06-09 22:15:48.687 htmlParser[3415:207] {
}
 ・
 ・

 例えば、送られてくるHTML文は
<!DOCTYPE html PUBLIC "-//W3C//DTD
XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />



  というようになってるんですが、これが一つ一つタグごとに分解されてdidStartElement:メソッドが呼び出されるわけで、上の<html>タグなら
html
2010-06-09 22:15:48.685 htmlParser[3415:207] {
lang = ja;
"xml:lang" = ja;
xmlns = "http://www.w3.org/1999/xhtml";
}

 というように綺麗に分解されて入ってくる。
 なのでxmlnsには何が設定されてるのかなと思ったら
NSString* urlString = [attributeDict valueForKey:@"xmlns"];

 とするだけで、urlStringに"http://www.w3.org/1999/xhtml"という文字列が入るわけです。

 で、今回のこの花は?サーバーはCSSを導入(その(201) CSSでいくぜ)して部品ごとにdivタグがclass属性と一緒に指定されてるわけで、こいつに注目すれば割と単純に花の写真のURLや問い合わせ文を判別できるつーことになる。

$テン*シー*シー-1

 div class=thumbタグの中で現れるimgタグは写真へのURLをもってるとかa class=contentsタグは提案IDを含むURLだとか判断できるわけですな。(thumbFlagはインスタンス変数として定義)て感じでタグを分別していくと
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
NSString* className = [attributeDict valueForKey:@"class"];
if ([className isEqualToString:@"thumb"]) {
thumbFlag = YES;
}
if ([className isEqualToString:@"contents"]) {
printf("ID = %s\n", [[attributeDict valueForKey:@"href"] UTF8String]);
}
if (thumbFlag && [elementName isEqualToString:@"img"]) {
printf("image URL = %s\n", [[attributeDict valueForKey:@"src"] UTF8String]);
thumbFlag = NO;
}
[currentParsedCharacterData release];
currentParsedCharacterData = nil;
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (!currentParsedCharacterData) {
currentParsedCharacterData = [[NSMutableString alloc]init];
}
[currentParsedCharacterData appendString:string];
}

 コンソールには
image URL = ./img_xcc_test/20100516084849.JPG
ID = ./suggestlist.php?contribution_id=13
image URL = ./img_xcc_test/20100516084844.JPG
ID = ./suggestlist.php?contribution_id=12
image URL = ./img_xcc_test/20100516084839.JPG
ID = ./suggestlist.php?contribution_id=11
image URL = ./img_xcc_test/20100516084830.JPG
ID = ./suggestlist.php?contribution_id=10
image URL = ./img_xcc_test/20100516084815.JPG
ID = ./suggestlist.php?contribution_id=9
image URL = ./img_xcc_test/20100516084800.JPG
ID = ./suggestlist.php?contribution_id=8

 って感じの出力が得られるわけです。
 IDはNSScannerなんかを使って、数字だけ取り出す作業がいりますな。
 てか、花の写真のimgタグにはclass="photo"とか指定すれば、class=thumbを見張る必要さえ無い。いっそIDも
 <div class="row">
 あたりを拡張して
 <div class="row" contribution_id ="提案ID">
 とかにしますか。

 そこらへん、次回。

------------
サンプルプロジェクト:htmlParser02.zip