Микроформаты — это хорошо, просто и понятно.
Как можно извлечь микроформатированный контент из документа и сделать с ним миллион классных штук?
XSLT
Удобно для запросов в XML-документе на извлечение микроформатированного контента использовать выражения XPath.
Вот пример «ручной» работы с XHTML-документом, содержащим μf:
Пример работы с XPath
(Требуется утилита xmllint
из пакета libxml).
Запрашиваем страницу:
$ wget dzhus.org/author
Открываем XML-консоль:
$ xmllint --shell index.html
Устанавливаем префикс пространства имён, чтобы были видны XHTML-элементы:
/ > setns xhtml=http://www.w3.org/1999/xhtml
Запрашиваем элемент с именем класса «vcard» (вернёт все hCard на странице):
/ > xpath //xhtml:*[@class='vcard']
Object is a Node Set :
Set contains 1 nodes:
1 ELEMENT div
ATTRIBUTE id
TEXT
content=owner-vcard
ATTRIBUTE class
TEXT
content=vcard
Или из всех hCard на странице выбираем ссылки, у которых установлен XFN-аттрибут rel="me"
:
/ > xpath //xhtml:*[@class='vcard']/xhtml:a[@rel='me']
Object is a Node Set :
Set contains 1 nodes:
1 ELEMENT a
ATTRIBUTE class
TEXT
content=url
ATTRIBUTE rel
TEXT
content=me
ATTRIBUTE href
TEXT
content=http://dzhus.org
ATTRIBUTE title
TEXT
content=#D0#9B#D0#B8#D1#87#D0#BD#D1#8B#D0#B9 #D1#81#D0#B0#D0#B9#D1#82
В реальных задачах по извлечению μf применимы преобразования XML при помощи XSLT. В XSLT можно извлекать данные из исходного XML-документа с применением выражений XPath.
Использование XSLT позволяет разделить логику программной обработки страниц и непосредственно извлечение/форматирование данных, позволяет использовать одни и теже механизмы извлечения в разных программах на разных языках. Кроме того, XSLT просто ближе к предметной области, чем явное кодирование разбора XML-дерева в тексте обрабатывающей программы, ИМХО. Синтаксис выражений XPath также скрывает детали реализации сложной фильтрации и выборки элементов из XML.
Недостатком XSLT является достаточно громоздкий синтаксис (в общем случае это пипец полный, но увы — XML =).
Идея оказалась не нова; уже существуют некоторые основанные на XSLT средства преобразования микроформатированных данных из страниц.
Микроформаты, GRDDL и Семантическая Паутина
Поскольку микроформаты — маленький промежуточный шаг на пути к Semantic Web, по-хорошему нужно использовать GRDDL — специальный механизм обозначения RDF-совместимых данных в XML-документе, а при помощи GRDDL однообразно извлекать микроформатированный контент. Возможно, GRDDL обеспечит тот будущий великий перенос данных на сверхвысокий уровень представления, к которому сейчас готовятся многие люди, маленькими шажками (например, используя микроформаты) увеличивая «семантичность» данных. На страницах w3.org предлагается интересный обзор возможных случаев использования GRDDL.
С RDF-данными можно работать на гораздо более высоком уровне, применяя специальные языки запросов. По сравнению с возможностями RDF-технологий копание в коде веб-страниц при помощи XSLT выглядит минимум суетливо.
На землю
Semantic Web — конечно великая цель и очень хорошо, но большинство двуногих обезьян, создающих страницы для Сети не могут написать даже well-formed документ (не говоря уже о валидности), чтобы хоть как-то упростить жизнь создателям средств обработки данных. Так что бок о бок с XML-парсерами существуют «real-world» парсеры даже не HTML, а чего-то непонятного (часто это деликатно называется Tag Soup, на самом деле это обычно блевота из тэгов), которые пытаются разобрать и угадать как можно больше из того некорректного кода, который часто встречается в Интернете, как это делают браузеры. Эвристика, болото частных случаев и попытка заочно вправить мозги задним числом верстальщикам — в итоге не всегда устойчивый результат и непрозрачная логика.
Тем не менее, я представлю простой пример основанного на XSLT обработчика распределённых социальных сетей, основанных на XFN. Во время обработки буду также учитывать rel-tag.
Реализация
Инструменты
В libxml есть возможности «восстановления» «повреждённого» кода с веб-страниц. Там всё равно достаточно строгий парсер, хотя с включённым восстановлением удаётся увеличить количество разбираемых страниц в несколько раз. Тем не менее, не удаётся разобрать до 35% процентов веб-страниц. Было бы удобно, однако, использовать libxml, поскольку в ней также очень быстрый XSLT-процессор.
Tidy — то, что нужно. Tidy позволяет с большой долей успеха формировать из побитых веб-страничек валидные XHTML-документы.
Для извлечения микроформатированного контента будет использоваться простой стилевой файл XSL.
Принцип работы
В принципе, схема рекурсивной обработки XFN должна быть уже очевидна. Программа будет принимать пару параметров — URL «точки входа» и глубину обработки. Страница, запрошенная по переданному в качестве параметра URL, будет обработана Tidy и разобрана парсером libxml, после чего на неё будет наложено XSL-преобразование. Предлагается в качестве результата обработки каждой страницы использовать промежуточный XML-формат, простой и удобный для последующей обработки.
XSL-преобразование, применяемое к каждой странице, будет извлекать интересующий микроформатированный контент, в числе которого будут размеченные с учётом XFN ссылки. Список этих ссылок составит новый набор страниц для обработки, к каждой из которых вся схема будет применена аналогично. Таким образом, используется тип рекурсивной обработки «в глубину» (а не «в ширину»).
На каждом шаге обработки будет проверяться текущая «глубина» (то, насколько в работе программа ушла от «нулевого уровня» — самой первой страницы). При достижении максимально допустимой глубины уже не будет производиться поиск новых страниц для обработки.
Страницы, находящиеся сразу за самым глубоким уровнем, однако, всё равно будут запрошены и обработаны, но по упрощённой схеме: будет извлечён лишь заголовок страницы. Это очень расточительно (запрос, обработка страницы лишь для извлечения значения <title></title>
!), но в таком случае будет известен заголовок каждой страницы, на которую ссылается любая другая в обрабатываемой сети. Это очень полезно для генерации карты сети по результатам работы программы (если бы не запрашивались заголовки страниц на «границе» обработки, пришлось бы в качестве названий узлов на границе карты использовать их URL — в большом количестве выглядит убого и сильно мешает восприятию; впрочем, заголовки страниц в качестве меток — тоже громоздко).
Результатом работы предполагается XML-файл со списком обработанных страниц. В каждом элементе будут содержаться приемлемо организованные извлечённые микроформатированные данные.
<?xml version="1.0"?>
<network>
<site url="http://bopik.com/">
<title>Bopik's Writings</title>
<rss>http://bopik.com/feed.xml</rss>
<relations>
<rel url="http://hardwaremaniacs.org/blog" type="friend colleague" />
<rel url="http://qurinal.com" type="date neighbor" />
</relations>
<tags>
<tag>violence</tag>
<tag>microformats</tag>
<tag>phtagn</tag>
</tags>
</site>
<site url="http://qurinal.com">
<title>qUrinal: best info source for developers</title>
</site>
<site url="http://hardwaremaniacs.org/blog">
<rss>http://hardwaremaniacs.org/feeds/blog/25</rss>
<title>Harware Maniacs</title>
</site>
</network>
Он, в свою очередь, подлежить дальнейшей обработке с целью получения более осязаемых результатов.
Рабочий пример
Пример исходников доступен по адресу http://github.com/dzhus/xfn-spider (лучше брать самую последнюю ревизию):
mf-extract.xsl и mf-extract-bottom.xsl — пара простых стилей для извлечения µf.
mf-extract-bottom.xsl
применяется к страницам, которые находятся на уровне глубины обработки после максимального и извлекает только заголовок.get-urls.xsl накладывается на результат работы
mf-extract.xsl
и формирует список URLов для последующей обработкиpostprocess.xsl мочит дубликаты с разными URL-ами, но одинаковыми заголовками страницы. Достаточно успешно.
xfn-spider.py — пример обёртки на Python вышеупомянутых четырёх стилей. Думаю, что исходник вполне понятен, и его можно легко перенести на другой язык (включая enterprise-level языки типа whitespace).
Работает так:
$ python xfn-spider.py http://entry-point.com 5 > output.xml
Отладочные сообщения выводятся на stderr
и не попадают в output.xml
.
Можно добавлять дополнительные действия по извлечению контента на уровне XSL-преобразований, не меняя Python-обёртку: механизм отделён от политики.
Пояснения также есть в файлах README и TODO.
Что делать дальше
Полученный в результате работы output.xml
— бесполезный полуфабрикат.
Можно сделать с ним что-нибудь прикольное.
Например, визуализировать.
Many eyes
Many eyes — инициатива IBM по созданию общественного ресурса по обмену статистическими данными и предоставлению средств визуализации этих данных. Короче говоря, можно туда загрузить свой dataset и визуализировать его. Возможностей в тысячу раз меньше, чем у нормальных средств визуализации (R, OpenDX или ROOT), но в Many Eyes дан упор на общественность процесса и «collaborating». И ещё там везде Java. Корпорэйты, что с них взять.
Карта XFN
Мне приглянулась имеющаяся там визуализация «Network»: более-менее нормальные картинки, сглаживание, красивая подсветка и всё такое. На входе для визухи сети нужен список пар — соединённых узлов, типа:
Петя Вася
Коля Вася
Вася Настя
Петя Настя
Колонки разделены символом табуляции.
Из полученного в результате работы xfn-spider.py
XML-я такой набор пар несложно получить ещё одним XSL-преобразованием (лежит в репо под именем make-manyeyes-network.xsl):
$ xsltproc make-manyeyes-network.xsl output.xml > many-eyes.txt
$ cat many-eyes.txt
Полученный набор данных можно загрузить на Many Eyes и получить интерактивненькую карту XFN (Челик сказал «woha»):
(Эта визуализация очень неполная и была получена на начальном этапе работы над описываемыми инструментами; в реальности узлов гораздо больше.)
Минус — тип связей никак не используется (впрочем, при большом количестве узлов разноцветные рёбра читаются ещё хуже просто чёрных).
Облако тэгов на всю XFN
Помимо XFN-ссылок извлекались также и размеченные при помощи rel-tag тэги на сайтах. Можно из результирующего XML просто дёрнуть их список и получить облако тэгов (HEY LOOK I’M WEB 2.0!).
Пусть у меня есть output.xml
— результат xfn-spider.py
. Тэги можно вырезать и простым грепом, но я снова хочу использовать Many Eyes и визуализацию «Tag Cloud», для неё лучше подготовить не просто список тэгов, а пары «тэг — количество упоминаний». Для этого снова можно применить совсем примитивное XSL-преобразование make-manyeyes-tags.xsl:
$ xsltproc make-manyeyes-tags.xsl output.xml > tags.txt
$ cat tags.txt
craig ferguson 1
cryotherapy 1
cryptography 2
culture 2
danny hillis 1
data mining 1
data web 2
data-web 4
Используя такой набор данных, можно получить подобное «облако»:
Graphviz
Рисовать сети на Many Eyes — забавно, но есть Graphviz — славный набор средств для качественной отрисовки графов. Можно преобразовывать описание XFN в формат DOT, который принимают программы Graphviz. Это имеет смысл при обработке особо больших сетей (на глубину >20), а то, скажем апплет Many Eyes может поперхнуться таким количеством узлов в сети.
Создание файла OPML со списком лент XFN
Тут тоже всё понятно. На большинстве сайтов уважают RSS и в коде есть указание на RSS-ленту:
<link rel="alternate" type="application/rss+xml" href="/blah-blah-feed.xml" />
При обработке XFN нужно выжимать из страниц как можно больше информации, если она удобно размечена.
Есть OPML — достаточно широко используемый XML-формат для обмена структурированными списками. Часто применяется для импорта/экспорта списков RSS/Atom-лент и обмена списками лент между разными RSS-читалками.
Так вот, выгодно при обработке сети заодно для каждого сайта сохранять ссылку на его RSS-представление, потом из списка RSS-представлений генерить файл OPML (или другого формата), чтобы потом его использовать для импорта в любимой RSS-читалке (например, Google Reader) и читать всю тусовку!
make-opml.xsl does the job:
$ xsltproc make-opml.xsl output.xml > feeds.opml
После чего feeds.opml
можно импортировать прямиком в RSS-ридер.
Однажды собрав список лент и получив доступ к «синдицированному» контенту, можно тоже отслеживать всякие тренды и подбивать статистику по всей XFN.
Недостаткомъ подхода является то, что не все ставят ссылку на RSS-представление в надлежащее место — тэг <link>
.
Если во время обработки XFN не просто собирать XFN-ссылки, но ещё и извлекать размеченные при помощи OPML блогроллы (понятно, что список XFN-ссылок на друзей и просто блогроллы не всегда совпадают) — toodoo.ru становится ненужным :)
Обработка социальных сетей
Однажды я поставил обработку на глубину 30, но через десять минут понял, что такого количества трафика у меня нет: уже на третьем уровне паучок вышел на чей-то профиль в twitter.com, а там на каждой странице участника список друзей размечен при помощи XFN и модно иметь по 30 «друзей» :) В общем, через час просто надоело ждать :- Как-то странно вышло, до этого при тестировании робот не выходил на Twitter-овские связи. Quick googling показало, что как раз в этот день на Twitter внедрили микроформаты
Так что визухи, ссылки на которые были выше — весьма неполные :)
Ясно что уже 30³ — это 27000 и до фига трафика и времени. Так что социальные сети обрабатывать стоит с осторожностью и, похоже, XFN-ссылки — не лучший способ для этого (слишком ресурсоёмко).
В общем, обработка XFN с точкой входа на том же http://tantek.com на глубину хотя бы 7 — задача явно не для PC, причём именно из-за того, что робот выходит в Twitter, где сверхплотные и сверхгустые XFN-связи.
Вот, что мне удалось в итоге получить (глубина всего лишь 3):
Недостатки использования XSLT
Fucking slow! Задержки сети при получении всей страницы — огромные! Обработки сети с глубиной 10 — дело многих минут. Основные проблемы — когда попадается особо тормозной хост, запрос страницы с которого идёт чуть ли не десять секунд. Сам парсинг занимает очень мало времени. В данном случае выгодно держать на каждом что-то типа кэша, как в XFN crawler, но это убивает соль микроформатов — простоту добавления и редактирования, и это лишний связующий уровень, снижающий прозрачность технологии.
Есть баги, связанные с обработкой разных ссылок на одни и те же ресурсы. Это просто сложно фильтровать. Я применил такую политику: из группы страниц с одинаковыми заголовками выбирается та, у которой больше всего связей. Она остаётся в результирующем файле без изменений. Все остальные лишаются списка тэгов и списка связей, для них остаются только заголовки. В итоге получается достаточно разумно и почти ничего не ломается. Фильтрация происходит на уровне postprocess.xsl
. Отладка обработки затрудняется значительными временными затратами на извлечение данных.
Стандартная отмазка от автора
(афтара?)
Я не знаю XSLT — в стилях есть ужасные места (например, обработку XFN-ссылок, возможно, было бы проще сделать с использованием регулярных выражений из EXSLT; текущий вариант создан под воздействием grokXFN.xsl — стиля для преобразования XFN в FOAF).