Разберём один из способов добавить Linked Data на обычный веб-сайт, на примере интернет-магазина.
Есть интернет-магазин «Рога и копыта», в котором продаётся чудо-молоток. Наша задача — опубликовать машино-читаемые данные о магазине (как компании) и товаре в соответствии с принципами 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.
Обратите внимание, что мы используем одновременно термины из трёх словарей — 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> .
Обратите внимание на выделенные части.
rdfs:label) — на русском и на украинском. Язык «подхватывается» из ближайшего атрибута xml:lang в коде страницы. В нашем случае этот атрибут объявлен на элементе html (то есть мы заявляем, что вся страница написана по-русски), поэтому все текстовые свойства получают язык ru.^^, например: "1200"^^xsd:integer.a — просто сокращение для rdf:type.Итак, мы «малой кровью» добавили Linked Data в наш интернет-магазин. Словари GoodRelations, FOAF, vCard и RDFS дали нам готовые термины, с помощью которых мы описали товар и фирму. А стандарт RDFa позволил разметить уже существующие страницы так, чтобы выделить в них свойства предметов. При этом мы почти не дублировали данные, а только прикрепляли атрибуты RDFa к уже имеющимся элементам страницы.
Это далеко не единственный способ публикации Linked Data. С помощью Triplify можно непосредственно представлять в виде LD записи из реляционной базы данных. Для некоторых популярных веб-приложений есть модули, которые сами публикуют всю нужную информацию. Наконец, ничто не мешает вручную размечать отдельные документы, как сделано, например, на linked-data.ru.