Публикация Linked Data

22 июля 2010 года

Разберём один из способов добавить Linked Data на обычный веб-сайт, на примере интернет-магазина.

Содержание

  1. Постановка задачи
  2. Главные решения
  3. Описание товара
  4. Описание фирмы
  5. Проверка
  6. Подводя итоги
  7. Дальше

Постановка задачи

Есть интернет-магазин «Рога и копыта», в котором продаётся чудо-молоток. Наша задача — опубликовать машино-читаемые данные о магазине (как компании) и товаре в соответствии с принципами Linked Data. Предполагаем, что сами данные хранятся в некой базе данных, а страницы генерируются по шаблонам с помощью какого-то движка.

Разумеется, мы не хотим ради LD переделывать весь сайт. Чем меньше придётся менять — тем лучше.

Главные решения

Для начала нам нужно выбрать словари, из которых мы возьмём термины для описания наших предметов — магазина и товара. (Также встречается понятие «онтология» — это разновидность словаря.) Очень желательно использовать известные и устоявшиеся словари — если, конечно, они есть для данной предметной области — потому что такие словари с наибольшей вероятностью будут «понятны» программам-клиентам. Если же подходящего словаря нет, можно создать и свой, пользуясь стандартами RDF Schema и OWL.

Мы в основном будем использовать словарь GoodRelations, в котором есть всё необходимое для описания фирмы, товаров и точек продаж и обслуживания. Но совершенно необязательно, да и невозможно, ограничиваться только одним словарём — напротив, можно и нужно сочетать термины из разных словарей подходящим образом.

Далее нужно определиться с тем, как мы «упакуем» RDF-описания магазина и товаров. Мы можем опубликовать их в отдельных ресурсах, независимых от HTML-страниц, а можем встроить прямо в страницы. Последнее делается с помощью стандарта RDFa — «RDF в атрибутах» — который позволяет записывать RDF-описания в атрибутах любого HTML- или XML-документа. RDFa позволяет разметить уже имеющиеся на страницах данные так, чтобы они стали машино-читаемыми. То есть для внедрения RDFa достаточно изменить шаблоны страниц магазина. Поэтому в наших условиях это наилучший выбор.

Linked Data требует от нас, чтобы адреса (URI) товара и магазина отличались от адресов HTML-страниц, на которых они описаны. Здесь у нас есть два варианта. Мы можем придумать для этих предметов совершенно отдельные адреса и поставить с них перенаправления на адреса страниц. Либо мы можем формировать «адреса с решёткой» (#): например, если http://linked-data.ru/example/ — главная страница интернет-магазина, то адрес компании может быть http://linked-data.ru/example/#company. Оба этих подхода позволят клиентам находить описания предметов, зная только их адреса. Но у второго способа — с решёткой — есть важное для нас преимущество: не нужно настраивать веб-сервер, чтобы он делал перенаправления с одних адресов на другие. Поэтому мы назначим компании адрес http://linked-data.ru/example/#company, а товару — http://linked-data.ru/example/products/1/#product.

Описание товара

Начнём с товара. Ниже показан полный код страницы http://linked-data.ru/example/products/1/.

<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
    "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru"
      version="XHTML+RDFa 1.0"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
      xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
      xmlns:gr="http://purl.org/goodrelations/v1#"
      xmlns:foaf="http://xmlns.com/foaf/0.1/">
    
    <head>
        <title>Молоток Alabama Instruments HS-919</title>
    </head>
    
    <body about="#product" typeof="gr:Offering">
        <h1 property="rdfs:label">Молоток Alabama Instruments HS-919</h1>
        <p rel="foaf:depiction">
            <img resource="photo.jpg" src="photo.jpg"
                 alt="Молоток красив и цветаст" />
        </p>
        <p property="rdfs:comment">
            Молоток HS-919 — новейшее предложение от Alabama Instruments.
            Оснащён системой SmartThump™, обеспечивающей наилучшую точность
            вбивания гвоздей.
        </p>
        <p rel="gr:hasPriceSpecification">
            Цена:
            <span about="#price">
                <span property="gr:hasCurrencyValue"
                      datatype="xsd:integer">1200</span>
                <span property="gr:hasCurrency" content="RUR">руб.</span>
            </span>
        </p>
        <p rev="gr:offers">
            <a href="/example/#company">
                Назад в интернет-магазин «Рога и копыта»
            </a>
        </p>
    </body>
    
</html>

Как видим, в основе разметки страницы лежит привычный язык XHTML, к которому добавлены атрибуты RDFa (в коде они выделены).

Адреса в RDF часто сокращают с помощью префиксов. Здесь они объявлены на элементе html (в атрибутах xmlns:rdfs, xmlns:gr и так далее). Принцип очень прост: к адресу, который соответствует префиксу, приписывается то, что стоит после двоеточия. Например, rdfs:label — сокращённая запись адреса http://www.w3.org/2000/01/rdf-schema#label.

Атрибут about на элементе body указывает адрес предмета, который описывается в этом элементе. В нашем случае адрес получается http://linked-data.ru/example/products/1/#product. Здесь же, в атрибуте typeof, можно указать класс, к которому принадлежит предмет. Классы в Linked Data в основном используются для упорядочивания и поиска информации, и зачастую их можно не указывать. В RDF принадлежность к классу (которых может быть несколько) представляется свойством rdf:type. В нашем примере товар имеет класс gr:Offering, который в словаре GoodRelations соответствует предложению (товара или услуги).

Элемент h1 помечен атрибутом property, в котором указано свойство rdfs:label. Это универсальное свойство для названия или надписи к любому предмету. Содержимое элемента h1 становится значением этого свойства. То есть, в нашем RDF-описании появляется стрелка, ведущая от товара к строке «Молоток Alabama Instruments HS-919», и адрес на этой стрелке — http://www.w3.org/2000/01/rdf-schema#label.

Чтобы связать предмет с другим предметом, используется атрибут rel (от relation). Адрес предмета указывается в атрибуте resource (на элементах a достаточно href). В нашем примере товар связан свойством foaf:depiction («изображение») с ресурсом http://linked-data.ru/example/products/1/photo.jpg, то есть со своей фотографией.

С ценой всё немного сложнее. Цена в GoodRelations представляется как отдельный предмет со свойствами: валюта и величина. В RDFa мы записываем это как «вложенный» предмет: на элементе span ставим атрибут about, заявляющий, что данный элемент описывает цену с адресом http://linked-data.ru/example/products/1/#price. Для значения свойства gr:hasCurrencyValue («величина цены») прописываем тип данных — xsd:integer (типы берутся из стандарта XML Schema). А значение свойства gr:hasCurrency («валюта») мы указываем явно в атрибуте content, так как оно отличается от содержимого элемента — человеко-читаемого сокращения «руб.»

Наконец, мы записываем, какая компания предлагает данный товар. Для этого используем атрибут rev (от reverse relation), который аналогичен rel, но ведёт стрелку в обратном направлении. Получается стрелка gr:offers («предлагает») от компании http://linked-data.ru/example/#company к предложению http://linked-data.ru/example/products/1/#product.

С помощью атрибутов RDFa мы добавили в XHTML-страницу машино-читаемые данные, образующие ориентированный граф.

Обратите внимание, что мы используем одновременно термины из трёх словарей — GoodRelations, FOAF и RDF Schema (RDFS) — а также специальное свойство rdf:type.

Описание фирмы

Теперь аналогичным образом опишем саму фирму на странице http://linked-data.ru/example/.

<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
    "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru"
      version="XHTML+RDFa 1.0"
      xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
      xmlns:gr="http://purl.org/goodrelations/v1#"
      xmlns:vcard="http://www.w3.org/2006/vcard/ns#">
    
    <head>
        <title>Интернет-магазин «Рога и копыта»</title>
    </head>
    
    <body about="#company" typeof="gr:BusinessEntity">
        <h1 property="rdfs:label">Рога и копыта</h1>
        <p property="rdfs:comment">
            Интернет-магазин «Рога и копыта» — ведущий поставщик товаров для
            всевозможных целей.
        </p>
        <p rel="vcard:tel">
            Звоните нам:
            <span about="#tel" property="rdf:value">+7 495 123 45 67</span>.
        </p>
        
        <h2>Популярные товары</h2>
        <ul rel="gr:offers">
            <li>
                <a href="products/1/#product">
                    Молоток Alabama Instruments HS-919
                </a>
            </li>
        </ul>
    </body>
    
</html>

Здесь мы указываем телефон нашего магазина с помощью словаря vCard (да, того самого, только записанного в RDF). Как и цена товара, телефон записывается в виде вложенного объекта, которому мы присвоили адрес http://linked-data.ru/example/#tel. Это позволяет при необходимости указывать тип телефона — рабочий, мобильный и так далее. Сам же телефонный номер связан с предметом #tel стандартным свойством rdf:value («значение»).

Проверка

Теперь, когда данные записаны и опубликованы, неплохо бы проверить, что мы сделали всё правильно.

Во-первых, можно прогнать наши страницы через валидатор W3C, который «знает» формат XHTML+RDFa и умеет проверять страницы на соответствие его определению (DTD).

Но валидатор проверяет наш код только на уровне XML, а мы хотим ещё и удостовериться, что правильно записали все свойства объектов, то есть получили правильные описания. Для этого воспользуемся службой RDFa Distiller. В поле адреса указываем http://linked-data.ru/example/products/1/, в поле «Output Format» выбираем Turtle, и нажимаем «Go!». Получим файл с описанием товара, «выжатым» из атрибутов RDFa и сохранённым в формате Turtle. Это примерно следующий код:

@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix gr: <http://purl.org/goodrelations/v1#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xhv: <http://www.w3.org/1999/xhtml/vocab#> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<http://linked-data.ru/example/#company>
    gr:offers <http://linked-data.ru/example/products/1/#product> . 

<http://linked-data.ru/example/products/1/#price>
    gr:hasCurrency "RUR"@ru ;
    gr:hasCurrencyValue "1200"^^xsd:integer . 

<http://linked-data.ru/example/products/1/#product>
    a gr:Offering ;
    rdfs:label "Молоток Alabama Instruments HS-919"@ru ;
    gr:hasPriceSpecification <http://linked-data.ru/example/products/1/#price>;
    rdfs:comment """
            Молоток HS-919 — новейшее предложение от Alabama Instruments.
            Оснащён системой SmartThump™, обеспечивающей наилучшую точность
            вбивания гвоздей.
       """@ru ;
    foaf:depiction <http://linked-data.ru/example/products/1/photo.jpg> .

Обратите внимание на выделенные части.

Подводя итоги

Итак, мы «малой кровью» добавили Linked Data в наш интернет-магазин. Словари GoodRelations, FOAF, vCard и RDFS дали нам готовые термины, с помощью которых мы описали товар и фирму. А стандарт RDFa позволил разметить уже существующие страницы так, чтобы выделить в них свойства предметов. При этом мы почти не дублировали данные, а только прикрепляли атрибуты RDFa к уже имеющимся элементам страницы.

Это далеко не единственный способ публикации Linked Data. С помощью Triplify можно непосредственно представлять в виде LD записи из реляционной базы данных. Для некоторых популярных веб-приложений есть модули, которые сами публикуют всю нужную информацию. Наконец, ничто не мешает вручную размечать отдельные документы, как сделано, например, на linked-data.ru.

Дальше