어제 클리앙 모두의 공원 게시판은 NSString 관련 메소드만 가지고 파싱을 해 보았는데요. 코드도 꽤 길어지고 굉장히 무식한 방법이었습니다. 어제 포스팅 마지막에 언급했던대로 좀 더 편한 방법들을 살펴보려고 하는데요. hpple라는 API가 있습니다. 나온지도 오래되었고(업데이트 된지도...), 제가 원래 외부API를 잘 사용안하는데요. 뭐 대체제가 없다보니 써 보겠습니다. 일단 아래 주소에서 받으실 수 있구요. https://github.com/topfunky/hpple 프로젝트에 6개의 파일을 포함시켜 주시면 됩니다. TFHpple.h TFHpple.m TFHppleElement.h TFHppleElement.m XPathQuery.h XPathQuery.m 그리고 환경설정을 두개 해 주셔야 하는데 PROJECT환경설정에서(Build Settings) All을 누르시면 여러가지 설정들을 찾아보실 수 있는데요. 설정해야될 것은 아래와 같습니다. Header Search Paths : ${SDKROOT}/usr/include/libxml2 Other Linker Flags : -lxml2 그리고 마지막으로 html파싱이 이루어지는 프로젝트에 TFHpple.h를 import 시켜주시면 설정은 다 끝났습니다. 프로젝트에 포함시킨 소스파일들을 한번 살펴보면 실제 파싱은 XPathQuery에서 이루어 지고 있음을 확인할 수 있습니다. 다른 소스들도 복잡하지 않으니 한번 살펴보시면 좋을것 같고요. 어제 클리앙 모공을 파싱했던 부분 기억나시나요. 가져온 데이터가 게시물제목, 링크, 아이디, 조회수, 글쓴시간이었는데요. 실제 데이터 파싱하는 부분만 구현 해 보겠습니다. // 클리앙 모공 NSString * path = @""; NSError * error; NSString * stringFromURL = [[NSString alloc] initWithContentsOfURL:[NSURLURLWithString:@"http://clien.career.co.kr/cs2/bbs/board.php?bo_table=park"] encoding:NSUTF8StringEncodingerror:&error]; if(stringFromURL == nil) { NSLog(@"Error reading URL at %@\n%@", path, [error localizedFailureReason]); } 윗 부분은 어제 했던 부분과 동일하고요. NSData * data = [stringFromURL dataUsingEncoding:NSUnicodeStringEncoding]; // Create parser TFHpple * xpathParser = [[TFHpple alloc] initWithHTMLData:data]; // 타이틀 & URL NSArray * elements = [xpathParser searchWithXPathQuery:@"//tr//td[2]//a"]; for(int i = 0 ; i < [elements count] ; i++) { NSLog(@"%@", [[elements objectAtIndex:i] content]); // 타이틀 NSLog(@"%@", [[[elements objectAtIndex:i] attributes] valueForKey:@"href"]); // URL } 타이틀과 URL을 파싱해서 가져오는 방법입니다. 오렌지색으로 하이라이트 된 부분과 일치하는 형태를 찾아서 elements에 배열형태로 넣는 방법인데요. tr태그 내부에 td태그가 2번 나온 뒤 a태그가 나오는 형태를 찾으라는 말인데요. <tr class="mytr"> <td>10816621</td> <td class="post_subject"> <a href='../bbs/board.php?bo_table=park&wr_id=10816621' >[차단공시]삭제요청에 의한 차단공시</a></td> <td class="post_name"><img src='../data/member/ci/cipher.gif' width='54' height='16' align='absmiddle' border='0'></td> <td><span title="2012-02-15 17:25:24">17:25</a></td> <td>1</td> </tr> <tr class="mytr"> <td>10816618</td> <td class="post_subject"> <a href='../bbs/board.php?bo_table=park&wr_id=10816618' >초고도 근시인데 라섹했습니다</a></td> <td class="post_name"><img src='../data/member/hi/hikari.gif' width='60' height='16' align='absmiddle' border='0'></td> <td><span title="2012-02-15 17:25:11">17:25</a></td> <td>10</td> </tr> <tr class="mytr"> <td>10816613</td> <td class="post_subject"> <a href='../bbs/board.php?bo_table=park&wr_id=10816613' >암스텔담에선 자전거 뒤에 타려면 이 정도 운동 신경은 가져야...</a></td> <td class="post_name"><span class='member'>Guylian</span></td> <td><span title="2012-02-15 17:25:01">17:25</a></td> <td>36</td> </tr> 위의 html 중 색칠해진 부분들이 배열에 들어가게 됩니다. 여기서 한가지 보셔야 할 것은.. content의 경우 해당 태그가 열리고 닫힌 사이의 값, 즉 아래에서 색칠된 부분입니다. <tr class="mytr"> <td>10816618</td> <td class="post_subject"> <a href='../bbs/board.php?bo_table=park&wr_id=10816618' >초고도 근시인데 라섹했습니다</a> <a 태그가 열리고, </a>로 닫힌 사이의 값입니다. attribute의 경우 해당 태그 내부에서 key=value 형태로 저장된 값입니다. NSDictionary형의 값이기 때문에 태그의 key가 딕셔너리의 키가 되고 태그의 value가 딕셔너리의 value가 됩니다. <tr class="mytr"> <td>10816618</td> <td class="post_subject"> <a href='../bbs/board.php?bo_table=park&wr_id=10816618' >초고도 근시인데 라섹했습니다</a> 여기에서는 오렌지색이 key가 되고 청록색이 value가 되기 때문에 [[[elements objectAtIndex:i] attributes] valueForKey:@"href"] 이런식으로 value를 가져올 수 있었습니다. 그럼 아이디를 가져오는 방법을 살펴볼텐데요. 아이디의 경우 두가지 형태가 있기 때문에 조금 복잡합니다. 일단 html 로 아이디가 스트링인 경우와 아이디가 이미지인 경우를 살펴보면요. <tr class="mytr"> <td>10816618</td> <td class="post_subject"> <a href='../bbs/board.php?bo_table=park&wr_id=10816618' >초고도 근시인데 라섹했습니다</a></td> <td class="post_name"><img src='../data/member/hi/hikari.gif' width='60' height='16' align='absmiddle' border='0'></td> <td><span title="2012-02-15 17:25:11">17:25</a></td> <td>10</td> </tr> <tr class="mytr"> <td>10816613</td> <td class="post_subject"> <a href='../bbs/board.php?bo_table=park&wr_id=10816613' >암스텔담에선 자전거 뒤에 타려면 이 정도 운동 신경은 가져야...</a></td> <td class="post_name"><span class='member'>Guylian</span></td> <td><span title="2012-02-15 17:25:01">17:25</a></td> <td>36</td> </tr> 3번째 td의 다음 element가 달라지는 것을 알 수 있는데요. 몇가지 방법이 떠오르긴 합니다만 직관적으로 스트링을 가져와서 nil이면 이미지로 판단해서 처리하는 방법으로 진행하겠습니다. // 아이디 elements = [xpathParser searchWithXPathQuery:@"//tr//td[3]"]; for (int i = 0; i < [elements count]; i++) { if(((NSString *)[[[[elements objectAtIndex:i] children] objectAtIndex:0]content]) != nil) { // 아이디가 스트링이면 스트링 출력 NSLog(@"%@" , ((NSString *)[[[[elements objectAtIndex:i] children] objectAtIndex:0] content]) ) ; }else { // 아이디가 이미지이면 링크 출력 NSLog(@"%@", ((NSString *)[[[[[elements objectAtIndex:i] children] objectAtIndex:0] attributes]valueForKey:@"src" ])); } } 위에서 한 방법과 똑같은 방법이기 때문에 따로 상세설명은 안하겠습니다. 이 코드 위쪽에 첨부된 html부분과 잘 비교해 보세요. 뭐 여기까지 왔다면 글 작성시간과 조회수는 쉽습니다. // 글작성시간 elements = [xpathParser searchWithXPathQuery:@"//tr//td[4]//span"]; NSLog(@"%@", [[elements objectAtIndex:0] content]); // 조회수 elements = [xpathParser searchWithXPathQuery:@"//tr//td[5]"]; NSLog(@"%@", [[elements objectAtIndex:0] content]); 이렇게 필요한 정보를 다 가져올 수 있었습니다. 다음번에는 정규식을 이용한 파싱을 해 보겠습니다. Stack overflow에서 보니 절대 html파싱은 정규식으로 하지 말라는 말이 많이 보입니다. 정규식이 다 좋은데 익숙해지지 않으면 읽기가 쉽지 않기때문에 그런데요. 사실 저도 정규식에는 근본없는 배움인지라...
'iOS' 카테고리의 다른 글
iOS의 다양한 뷰 전환 - 1. 모달 뷰 컨트롤러 전환과 효과 (0) | 2012.08.20 |
---|---|
UDID 대체 방법 -> MAC 어드레스 얻기 (0) | 2012.08.01 |
SQLITE3 DB 파일 복사 및 , Path가져오기 (0) | 2012.06.21 |
tabbar item에 들어갈 이미지 최대 사이즈는? (0) | 2012.06.20 |
APSN 설정 처리 (0) | 2012.06.20 |