Pull to refresh

Парсинг HTML в iOS приложении

Добрый день, уважаемые хабра-юзеры.

Сегодня на github я наткнулся на отличный HTML-парсер, коотрый смогут освоить даже новички в программировании под iOS.

Под хабракатом я расскажу вам, что это такое и с чем его едят.

Лучше всего показать его возможности выйдет на конкретном примере, поэтому, приступим.

Подготовка

  • Перенесите *.h и *.m файлы парсера в ваш проект
  • В настройках добавьте "/usr/include/libxml2" в поле «header search paths»
  • Добавьте фреймворк libxml2.dylib


Получаем HTML код

В качестве примера мы будем использовать мобильную версию хабрахабра, для этого с помощью ASIHTTPRequest отправим запрос по адресу m.habr.ru/?fl и получим требуемый нам html код.

По ASIHTTPRequest есть большое количество уроков, поэтому не вижу смысла описывать то, как я это делал, лучше сразу перейдем к цели нашей статьи — парсингу html.

Наконец-то парсим

Код мобильной странички Хабра выглядит примерно так:
...
<body>

<div class="tm"><a href="http://m.habrahabr.ru/" accesskey="2">μHabr</a></div>
<div class="c">
<ul class="tl">

<li>
<h3><a href="http://m.habrahabr.ru/post/136556/" class="t">Один дизайн или много?</a> ← <a href="http://m.habrahabr.ru/blog/web_design/">Веб-дизайн</a></h3>
<span><em>Exitmusic, вчера в 02:36</em> <a href="http://m.habrahabr.ru/post/136556/" class="comments">комментарии</a> (8)</span>
</li>

<li>
<h3><a href="http://m.habrahabr.ru/post/136517/" class="t">Что такое ITSM?</a> ← <a href="http://m.habrahabr.ru/blog/sysan4dummies/">Анализ и проектирование систем</a></h3>
<span><em>vvl, вчера в 11:55</em> <a href="http://m.habrahabr.ru/post/136517/" class="comments">комментарии</a> (3)</span>
</li>
</body>
...


Наша задача: получить для каждого поста название, прямую ссылку, имя автора и блог, в котором он написан.

Для начала получим код, который находится в «body» нашей странички (не забудьте импортировать оба *.h файла парсера):

NSError *error = nil;
    HTMLParser *parser = [[HTMLParser alloc] initWithString:html error:&error];
    
    if (error) {
        NSLog(@"Error: %@", error);
        return;
    }
    
    HTMLNode *bodyNode = [parser body];


Я думаю, вы уже заметили, что информация о каждом посте находится между тэгами «li». Следовательно, мы должны выделить эти участки html-кода, чтобы продолжить обработку. Сказано — сделано:
NSArray *postNodes = [bodyNode findChildTags:@"li"];

Я думаю, что проще всего будет собрать всю информацию о постах в NSMutableDictionary, чтобы в последствии мы могли легко с ней работать. Для этого инициализируем этот объект:
NSMutableArray *articlesDone = [[NSMutableArray alloc] init];

Анализируем код дальше и видим, что в блоке с каждым постом есть два элемента с тэгом «a». Первый — название поста, второй — название блога. Отсюда мы можем получить большую часть нужных нам данных: название поста, блога и ссылки на них. Это исполнит следующий код:

for (HTMLNode *postNode in postNodes) {
        NSMutableDictionary *article = [[NSMutableDictionary alloc] init];
        NSArray *aTags = [postNode findChildTags:@"a"];
        if (aTags.count > 0) {
            HTMLNode *aOne = [aTags objectAtIndex:0];
            NSString *nameOfArticle = [aOne contents];
            NSString *link = [aOne getAttributeNamed:@"href"];
            [article setObject:nameOfArticle forKey:@"title"];
            [article setObject:link forKey:@"link"];
            HTMLNode *aTwo = [aTags objectAtIndex:1];
            NSString *blogname = [aTwo contents];
            NSString *blogLink = [aTwo getAttributeNamed:@"href"];
            [article setObject:blogLink forKey:@"bloglink"];
            [article setObject:blogname forKey:@"blog"];
            [articlesDone addObject:article];
        }
    }


Итак, мы получили большую часть информации о посте, осталось только получить имя автора, но это оказалось более сложной задачей, ведь имя автора соединено с датой, поэтому нам понадобится использовать NSScanner, ведь ник отделен запятой. Итак, немного модифицируем нынешний код, чтобы получить недостающую информацию. В результате получаем:
 for (HTMLNode *postNode in postNodes) {
        NSMutableDictionary *article = [[NSMutableDictionary alloc] init];
        NSArray *titles = [postNode findChildTags:@"a"];
        if (titles.count > 0) {
            HTMLNode *aOne = [titles objectAtIndex:0];
            NSString *nameOfArticle = [titleOne contents];
            NSString *link = [titleOne getAttributeNamed:@"href"];
            HTMLNode *authorNode = [postNode findChildTag:@"em"];
            NSString *authorAndDate = [authorNode contents];
            NSScanner *scanner = [[NSScanner alloc] initWithString:authorAndDate];
            [scanner scanUpToString:@"," intoString:nil];
            NSString *author = [authorAndDate substringWithRange:NSMakeRange(0, scanner.scanLocation)];
            [scanner release];
            HTMLNode *aTwo = [titles objectAtIndex:1];
            NSString *blogname = [titleTwo contents];
            NSString *blogLink = [titleTwo getAttributeNamed:@"href"];
            [article setObject:blogLink forKey:@"bloglink"];
            [article setObject:blogname forKey:@"blog"];
            [article setObject:nameOfArticle forKey:@"title"];
            [article setObject:link forKey:@"link"];
            [article setObject:author forKey:@"author"];
            [articlesDone addObject:article];
        }
    }


Вот мы и получили интересующую нас информацию о постах с сайта m.habr.ru, при этом затратили очень мало усилий и времени.

Спасибо за внимание.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.