Парсим микроформаты

Автор оригинала: Brian Suda
  • Перевод
Микроформаты — это способ внедрения специфических семантических данных в HTML, который мы используем сегодня. Первый вопрос, который задал бы гуру XML: «А зачем применять HTML, если XML позволяет создать ту же самую семантику?» Я не буду перечислять все причины, по которым XML был бы лучшим или худшим выбором для кодирования данных, или же почему микроформатами был выбран HTML в качестве базы. Эта статья будет сосредоточена на том, как работают базовые правила парсинга, и как они отличаются от XML-ных.

Контактная информация в HTML



Один из самых популярных и укрепившихся в обиходе микроформатов — hCard. Это представление vCard в HTML («h» в hCard — сокращение от «HTML vCard»). Вы можете прочитать больше про hCard в вики по микроформатам. vCard содержит базовую информацию о человеке или организации. Этот формат широко используется в приложениях адресных книг, как способ резервного копирования контактной информации и обмена ей. По меркам Интернета это старый формат, его спецификация — RFC2426 1998 года. Это было до XML, посему синтаксис являет собой обычный текст с некоторыми разделителями и элементами начала и конца. Возьмём для примера мою информацию:

BEGIN:VCARD
FN:Brian Suda
N:Suda;Brian;;;
URL:http://suda.co.uk
END:VCARD


Этот файл vCard содержит элементы BEGIN:VCARD и END:VCARD, представляющие собой контейнер, благодаря которому парсер знает, когда остановить сбор данных. В одном файле может быть несколько vCard-ов, так что такой способ красиво группирует данные в чёткие vCard-ы. FN означает «форматированное имя», которое используется как имя для отображения. N — это структурированное имя, в котором закодированы собственно имя, фамилия, второе имя (отчество), префиксы и суффиксы, и всё это разделяется при при помощи точки с запятой. Наконец, URL — это адрес сайта, ассоциированного с этим контактом.

Если бы нам надо было кодировать всё это в XML, результат, наверное, выглядел бы примерно так:

<vcard>
    <fn>Brian Suda</fn>
    <n>
        <given-name>Brian</given-name>
        <family-name>Suda</family-name>
    </n>
    <url>http://suda.co.uk</url>
</vcard>


Давайте взглянем, как мы сможем разметить те же самые данные в HTML с использованием микроформатов, которые широко используют атрибуты rel, rev и class для кодирования семантики. Атрибут class используется почти тем же самым образом, каким элементы используются в XML. Так что предыдущий пример XML размечается в HTML вот так:

<div class="vcard">
    <div class="fn">Brian Suda</div>
    <div class="n">
        <div class="given-name">Brian</div>
        <div class="family-name">Suda</div>
    </div>
    <div class="url">http://suda.co.uk</div>
</div>


Если бы это было всё, на что способны микроформаты, это не было бы так интересно. Но нет, микроформаты позволяют извлекать пользу из семантики существующих элементов HTML, чтобы объяснить, где могут быть найдены закодированные данные. В этом примере каждый элемент — <div>, но это не обязательно. Это делает извлечение данных из HTML чуть более сложным для парсера, но автору документа становится проще. Микроформаты не заставляют авторов менять текущую структуру HTML или манеру публикации. В конце концов, людей, пишущих HTML, на порядки больше, чем писателей парсеров, так почему бы не упростить жизнь авторам?

Когда я смотрю на предыдущий пример XML, мне не нравится, что я вижу «Brian Suda» дважды, один раз в FN и потом ещё раз в N. С HTML это не проблема, мы можем совместить эти XML-элементы, используя разделённые пробелом значения атрибута class. Малоизвестный факт — атрибуты class, rev и rel могут иметь разделённый пробелом список значений. И если мы совместим FN и N, получим что-то вроде этого:

<div class="n fn">
    <div class="given-name">Brian</div>
    <div class="family-name">Suda</div>
</div>


Теперь и у свойства N по-прежнему остались дочерние свойства, и FN содержит то же значение, что и раньше. Как мы помним, HTML сжимает пробелы, так что значение FN по-прежнему «Brian Suda», даже несмотря на то, что оно разделено между двумя элементами с пробелами внутри div'ов.

Вот так мы обозначили способность соединять свойства с одинаковыми зачениями. Следующая вещь, которая мозолит мне глаза в XML-примере — это способ, которым отображается URL, он не выглядит естественным. В XML мы говорим о данных, но HTML показывается людям в браузере. По совпадению, у нас есть элемент <a>, у которого есть атрибут href, берущий значение URL, а также у него есть текстовое значение для отображения более дружественного человеку текста. Мы можем и дальше шлифовать наш HTML-пример, меняя элемент <div> на <a>:

<a class="n fn url" href="http://suda.co.uk">
    <span class="given-name">Brian</span>
    <span class="family-name">Suda</span>
</a>


После переключения на элемент <a> нам надо заменить дочерние div'ы на span'ы, потому что элемент <a> может содержать только дочерние элементы инлайн-уровня. Микроформаты не заставляют авторов использовать те или иные элементы, но рекомендуется использовать наиболее семантические для каждого случая. В случае URL лучше всего использовать <a>, поэтому правила парсинга слегка изменятся (мы обсудим это чуть позже).

Конечный микроформат hCard в HTML будет похож на следующее:

<div class="vcard">
    <a class="n fn url" href="http://suda.co.uk">
        <span class="given-name">Brian</span>
        <span class="family-name">Suda</span>
    </a>
</div>


По-моему, это куда более интуитивно, просто и компактно, чем пример XML в начале. Люди уже публикуют блогроллы и ссылки таким образом, и все браузеры распознают и стилизуют эту информацию, к тому же её легко поместить внутри фидов.

Парсим при помощи XSLT



Микроформаты рассчитаны на работу с HTML 4 и выше. Минус использования XSLT в том, что документ обязан быть корректно сформирован, что в HTML 4 не обязательно. В HTML 4 тэги <br>, <img> и <hr> могут использоваться без закрывающих тэгов. Если бы вы использовали для извлечения микроформатов другую технологию, наподобие REGEX-ов или DOM, это был бы другой вопрос, но с XSLT нам в первую очередь надо подчистить HTML. Для этого есть два простых способа: TIDY или функция вроде HTMLlib или loadHTML, любой из них загрузит HTML документ и преобразует его в допустимый для XSLT.

Теперь, когда мы знаем, что у нас корректно сформированный HTML-документ, мы можем начать извлечение данных микроформатов. Нижеприведённое — очень сырой XSLT, далёкий от совершенства, но для начала вам должно хватить. Для дальнейшей информации можно прочитать страницу вики microformats.org о парсинге, или же использовать шаблоны XSLT, выполняющие большинство тяжкой работы по извлечению данных (они доступны на hg.microformats.org).

Все данные hCard содержатся внутри элемента, имеющего класс «vcard». В нашем примере это <div>, но это может быть любой элемент, поэтому мы начнём с этого:

//*[@class="vcard"]


Это выражение XPath ищет любой элемент в дереве, у которого класс «vcard». С первого взгляда кадется, что это найдёт все hCard'ы, но проблема в том, что значение класса может быть списком значений, разделённых пробелами. Таким образом, код class=«vcard myStyle» не будет выбран этим выражением XPath. Чтобы исправить это, мы используем функцию contains:

//*[contains(@class,"vcard")]


Это уже лучше, теперь мы найдём любой элемент, в котором атрибут класса содержит «vcard». В выражении class=«vcard myStyle» успешно будет найдено «vcard», но есть ещё одна проблема. Функция contains небезопасна тем, что это поиск по подстроке. Так что class=«my-vcard» будет найден функцией contains() точно так же, как class=«vcard», несмотря на то, что «my-vcard» не является правильным именем свойства, которое обозначало бы, что перед нами микроформат hCard. Ложное совпадение. Чтобы исправить это, придётся слегка поколдовать и окружить искомые значения пробелами, и затем искать новое обёрнутое пробелами значение. Звучит сложно, но на деле это не так.

//*[contains(concat(" ",@class," "), " vcard ")]


С пробелами класс «my-vcard» выглядит как " my-vcard " и не будет содержать подстроку " vcard ", что решает проблему подстрок. В другом случае, класс «vcard myStyle» превратится в " vcard myStyle ", где содержится " vcard ", так что разделённые пробелами значения тоже находятся при помощи этой техники

Теперь, когда мы знаем, как найти данные, давайте пройдём по каждой hCard, используя XSLT, и начнём выводить их в виде vCard. К этому времени уже легко увидеть, как использование XSLT позволяет легко преобразовывать данные HTML в практически любой формат. Например, другой HTML, XML, RDF, текст vCard, CSV, SPARQL, JSON, или чего ещё ваше сердце пожелает.

Оператор for-each найдёт все hCard'ы на странице и создаст vCard для каждого. Создавая vCard, он применяет шаблоны для поиска внутри hCard, такие как FN, N и URL.
<xsl:for-each select="//*[contains(concat(" ",@class," "), " vcard ")]">
    <xsl:text>BEGIN:VCARD</xsl:text>
    <xsl:apply-templates />
    <xsl:text>END:VCARD</xsl:text>
</xsl:for-each>


FN — это простой шаблон, извлекающий значение элемента с классом, содержащим FN.

<xsl:template match="//*[contains(concat(" ",@class," "), " fn ")]">
    <xsl:text>FN:</xsl:text><xsl:value-of select="."/>
</xsl:template>


Шаблон N чуть сложнее. Во-первых, ему надо найти элемент с классом, содержащим N. Затем он ищет дочерние элементы, содержащие подсвойства N, такие, как имя и фамилия, и выводит их значения.

<xsl:template match="//*[contains(concat(" ",@class," "), " n ")]">
    <xsl:text>N:</xsl:text>
    <xsl:value-of select="//*[contains(concat(" ",@class," "), " family-name ")]"/>
    <xsl:text>;</xsl:text>
    <xsl:value-of select="//*[contains(concat(" ",@class," "), " given-name ")]"/>
    <xsl:text>;;;</xsl:text>
</xsl:template>


Шаблон для URL использует элемент choose, чтобы определить, где находится наиболее семантическая информация для значения URL. Он проверяет, является ли элемент с классом «url» элементом <a>. Если да, то значение адреса извлекается из href, в ином случае используется строковое содержимое.

<xsl:template match="//*[contains(concat(" ",@class," "), " url ")]">
    <xsl:text>URL:</xsl:text>
    <xsl:choose>
        <xsl:when test="local-name() = 'a'">
            <xsl:value-of select="@href"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="."/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>


Элемент <a> и многие другие носят в себе свою семантику. В оригинальном HTML-примере URL был кодирован при помощи <div>, в этом случае, содержимое было бы извлечено, и значение URL было бы таким же. Это лишь один из многих признаков, по которым микроформаты отличаются от XML. Парсинг данных микроформатов зависит от того, в каком типе и элементе HTML они были закодированы.

Это был очень краткий обзор извлечения данных из микроформата. Есть и ещё правила, зависящие от типа свойства vCard, и на которых строится элемент HTML. Для дополнительной информации обратитесь к вики по микроформатам, моей PDF-книге Using Microformats, или вы всегда сможете написать мне на email, а также подписаться на рассылку разработчиков микроформатов при наличии вопросов.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 13

    0
    Спасибо, отличная статья.
    А вы ничего не слыхали про Jena? там очень удобные есть инструменты для работы с микроформатами.
      0
      нет, надо будет погуглить. А что это - фреймворк какой-то?
      –5
      Знаю что не в тему. Но запостить немогу :(
      http://img.artlebedev.ru/news/2007/gm/g-m.gif
      Что это Лебедев Гугль невзлюбил :), чем интересно он ему насолил?
        +3
        Насолил тем что отказался заказывать дизайн у Лебедева (:
          0
          тем что гугль установит контроль над пользователями еще сильнее, чем некрософт
            0
            Угу. Лет через 5 (если не быстрее) начнутся первые звоночки.
            +1
            Скоро, когда роботы найдут это, artlebedev.ru удалят с индексации гугла. А потом гугл купит яндкес и удалит дизайн Лебедева. А потом гугл купит НЛО и удалит Лебедева.
            0
            <vxsl:alue-of select="@href"/>

            Вы немного опечатались.

            <xsl:value-of select="@href"/>
              0
              благодарю за указание! Уже поправила :)
              0
              Только я не вижу смысла из hCard генерировать vCard.
              Это будет очередной fallback. Пусть люди себе ставят расширения для браузеров лучше и там добавляют контакты туда куда надо или сохраняют vCard'ы для последующей работы с ними.
                0
                видимо, просто для примера - надо ж на чём-то простеньком учиться :)
                +1
                alert("BEGIN:VCARD\nFN:"+(t = $$('.username')[0].innerHTML)+"\nN:"+t.split(' ').reverse().join(';')+";;;\nURL:"+$$('.userinfo .userinfo_line .right a')[2].getAttribute('href')+"\nEND:VCARD");


                vCard для хабралюдей

                и почему не работает javascript:alert(... в адресной строке...
                  +3
                  Для программистов на Ruby есть замечательная библиотека mofo:
                  http://mofo.rubyforge.org/

                  А вот так можно ей пользоваться:

                  $ sudo gem install mofo -y

                  $ irb -rubygems
                  >> require 'mofo'
                  => true
                  >> fireball = hCard.find 'http://flickr.com/people/gruber/'
                  => #
                  >> fireball.properties
                  => ["fn", "logo", "url", "n", "adr", "title", "nickname"]
                  >> fireball.nickname
                  => "gruber"
                  >> fireball.url
                  => "http://daringfireball.net/"
                  >> fireball.n.family_name
                  => "Gruber"
                  >> fireball.title
                  => "Raconteur"
                  >> fireball.adr.locality
                  => "Philadelphia"
                  >> fireball.logo
                  => "http://static.flickr.com/9/buddyicons/44621776@N00.jpg?1117572751"

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое