Теория и практика Java: Анализ экранных данных с помощью XQuery

XQuery упрощает извлечение и трансформацию HTML

XQuery является стандартом W3C по извлечению информации из XML-документов, охватывающим 14 рабочих проектов. Хотя в большинстве случаев XQuery нацелен на осуществление запросов к слабоструктурированным данным обширных баз данных документов, он может быть также эффективен и для решения более повседневных задач. В этом месяце Брайан Гетц, ведущий рубрики "Теория и практика Java", покажет Вам эффективные способы использования XQuery в качестве механизма анализа экранных данных HTML.

Брайан Гетц, главный консультант, Quiotix

Брайан Гетц (Brian Goetz) - консультант по ПО и последние 15 лет работал профессиональными разработчиком ПО. Сейчас он является главным консультантом в фирме Quiotix, занимающейся разработкой ПО и консалтингом и находящейся в Лос-Альтос, Калифорния. Следите за публикациями Брайана в популярных промышленных изданиях. Вы можете связаться с Брайаном по адресу brian@quiotix.com



27.02.2007

В прошлом месяце эксперт по технологии Java™ Сэм Пуллара (Sam Pullara) показал мне свой новый телефон с поддержкой Java - Nokia 6630. Он просто набит технологиями - встроенными JVM, GPRS, Bluetooth - но обладает стандартным недостатком, досаждающим всем смартфонам - ограниченным полезным пространством экрана. У некоторых Web-сайтов есть поддержка для телефонных браузеров, а встроенные браузеры пытаются эффективно воспроизводить страницы на маленьких экранах, но пытаться просмотреть обычную Web-страницу на экране телефона все равно, что втискивать слона на заднее сиденье Вашего автомобиля (к всеобщему неудовольствию - Вашему, машины и слона). Сэм разработал простое, элегантное решение для анализа экранных данных его любимых Web-сайтов и их переформатирования для небольших экранов.

Новаторский подход

Для извлечения данных из HTML-документов можно использовать различные подходы. Мне больше всего нравится подход, предложенный Сэмом - использовать XQuery и в качестве инструмента анализа экранных данных (для извлечения из страниц существующей информации), и в качестве инструмента таблицы стилей (для переформатирования данных таким образом, чтобы они умещались на странице без прокручивания). При помощи небольшой инфраструктуры и довольно простых выражений XQuery стало возможно извлекать существующую информацию - например, трафик, погоду и финансовые котировки - из многочисленных источников данных и удобно отображать их на экране телефона.

Я часто оказывался в ситуации, когда анализирующие экранные данные HTML-страницы казались разумным решением конкретной проблемы, но существует очень ограниченное число пакетов инструментальных средств анализа экранных данных на основе Javа. Существует много инструментов для парсинга HTML, но в большинстве случаев им не хватает достаточного количества абстрактных возможностей (что приводит к беспорядочному анализу экранных данных), и они ограничены повсеместным использованием плохо адаптируемого HTML, и практически не справляются с динамически порождаемыми страницами, чья структура может изменяться с течением времени.

Для преодоления пропасти между низкокачественным HTML и обширным набором инструментальных средств, обрабатывающих XML, Вам сначала придется конвертировать HTML в XML. Некоторые инструментальные средства могут Вам в этом помочь - набор инструментальных средств JTidy прекрасно выполняет работу и облегчает задачу. JTidy спроектирован для считывания HTML стандартного (то есть, плохого) качества и вывода чего-то более чистого (Вы можете задать опции), а также обеспечивает DOM-интерфейс для перемещения HTML-документов, которые можно передать в анализатор XML. Код в листинге 1 считает HTML-документ из InputStream и сгенерирует DOM-представление документа:

Листинг 1. Код для конвертации HTML в XML-совместимый DOM посредством JTidy
Tidy tidy = new Tidy(); 
tidy.setQuiet(true);
tidy.setShowWarnings(false);
Document tidyDOM = tidy.parseDOM(inputStream, null);

С помощью этой несложной трансформации Вы можете обрабатывать практически любую Web-страницу как XML-документ и использовать Ваши любимые XML-инструменты для извлечения данных - SAX, XSL, XPath, и так далее. Хотя XSL и может показаться вполне очевидным выбором, так как он спроектирован для извлечения информации из XML-документов с ее последующей трансформацией для представления, но, XSL довольно сложно освоить, если вы с ним ранее не работали, и даже простейшие XSL-трансформации могут оказаться достаточно запутанными. XPath является достойной альтернативой с точки зрения извлечения - которую используют и XSL, и XQuery для отбора содержимого - и Вы легко сможете использовать XPath для извлечения необходимых данных, а затем самостоятельно отформатировать HTML. XQuery позволяет все сделать гораздо быстрее и проще.


XQuery: очень краткий экскурс

XQuery был спроектирован для извлечения данных из потенциально обширных массивов данных XML. Входной массив данных не обязательно должен быть XML-документом, хотя и может являться таковым, но он также может представлять собой набор документов, которые были проиндексированы и сохранены в базе данных XML, или даже набор таблиц в реляционной базе данных. Как и в SQL, в XQuery есть функции для извлечения, суммирования, составления и объединения данных из различных массивов.

Так же, как и языки представления шаблонов, например, JSP, ASP или Velocity, XQuery объединяет элементы двух доменов - домена представления и домена вычисления - в одну объединенную синтаксическую структуру. В итоге любой XML-документ представляет собой истинное XQuery-выражение, которое находит свое значение. Имеются также и языковые операторы, например, "for" и "let", которые могут тесно сплетаться с элементами XML.

В листинге 2 показан пример XML-документа bib.xml, представляющего книжную библиографию. Я покажу Вам несколько быстрых выражений XQuery, чтобы дать представление о возможностях XQuery, а затем перейду к примерам анализа экранных данных. Обзор синтаксиса и случаев применения XQuery может занять сотни страниц - более подробные справочные материалы и примеры приведены в разделе Ресурсы.

Листинг 2. Пример библиографии XML
<bib>
    <book year="1994">
        <title>TCP/IP Illustrated</title>
        <author><last>Stevens</last><first>W.</first></author>
        <publisher>Addison-Wesley</publisher>
        <price> 65.95</price>
    </book>
    . . .  more books . . . 
</bib>

В листинге 3 показано XQuery-выражение, осуществляющее выборку всех книг, опубликованных издательством Addison-Wesley с 1991 г. Реализовано извлечение их названий с оформлением в виде маркированного (<ul>) списка. Переключатель с "режима представления" (данные будут напрямую переданы на выход, например, теги <ul> и <li>) на "режим кодирования" указан в фигурных скобках. Скрытое переключение с "режима кодирования " на "режим представления" выполняется немедленно после оператора возврата.

Листинг 3. Выражение XQuery для выбора названий книг в соответствии с критериями запросами
<ul>
{
  for $b in doc("bib.xml")/bib/book
  where $b/publisher = "Addison-Wesley" and $b/@year > 1991
  return
    <li>{ data($b/title) }</li>
}
</ul>

Синтаксическая структура запроса, начинающегося с "for" и часто называемого "Flower-выражением" (от FLWOR, аббревиатуры от for-let-where-order-return, выбирает последовательность XML-узлов из документа, в данном случае набор узлов <книга> из документа bib.xml при помощи выражения XPath, и далее отбирает те узлы, которые соответствуют указанным критериям запроса (издатель - Addison-Wesley, и год публикации - после 1991 г.) Для каждого из этих узлов он вычисляет выражение в операторе возврата, который в данном случае является комбинацией разметки (теги <li>) и кодирования (извлекает содержимое элемента <title (название)> каждого узла <book (книга)>).

Этот простой пример наглядно показывает несколько аспектов XQuery - объединение представления и кодирования в одном документе, использование XPath, использование подстановки (ссылки $b), нетривиальное выражение запроса, XQuery-функцию (data()), а также то, что структура выходного документа не обязательно должна соответствовать структуре входного документа. Все это свидетельствует о больших возможностях по обработке данных, заложенных в довольно компактном и достаточно легко читаемом запросе.

В листинге 4 показано еще более простое выражение XQuery, выдающее общее число различных издателей библиографии в одном элементе <count>. Как и в предыдущем примере, для выборки узлов здесь используется выражение XPath и применяются функции XQuery для выборки различающихся значений и подсчета количества узлов. Происходит расчет численного значения - число различных издателей в документе bib.xml.

Листинг 4. XQuery выражение для подсчета числа различных издателей
<count>
{
  let $d := distinct-values(doc("bib.xml")/book/publisher)
  return count($d)
}
</count>

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


Анализ экранных данных с помощью XQuery

Одна (из множества) проблем анализа экранных данных Web-страниц заключается в том, что зачастую у Web-страниц нет автоматически идентифицируемой структуры, и их структура может изменяться при редактировании контента сайта, или даже при вставке в страницу различного динамического контента (например, рекламного) при различных запросах. В результате зачастую Вам приходится догадываться о том, в каких частях страницы находится та информация, которую Вы хотите извлечь.

Курсы акций

Давайте начнем с извлечения текущей цены акций IBM с финансовой страницы Yahoo! (http://finance.yahoo.com/q?s=IBM). На этой странице находится множество материалов - заголовки новостей, реклама, финансовые данные - но нам нужны данные о курсах акций, которые расположены в ячейке таблицы, соседствующей с ячейкой "Last Trade (Последние сделки с ценными бумагами)." Запрос, показанный в листинге 5, выбирает все узлы <td>, в тексте которых содержится "Last Trade," и для каждого (я предполагаю, что только для одного) выдает строку таблицы, содержащую данные следующего узла <td>. Эти данные извлекаются при помощи функции data() в операторе return; иначе я бы получил больше, чем просто текст в узле <td>, я бы получил и всю разметку. (Единственной сложной частью данного запроса является text()[1], в которой функция text() сопоставляет все текстовые узлы элемента <td> - в нашем случае он единственный, но XQuery об этом не знает - и, следовательно, далее мне нужно поручить ему выбрать первый текстовый узел, прежде чем пытаться произвести текстовое сопоставление.) Пока на этой странице содержится ячейка таблицы с текстом "Last Trade", а последующая ячейка содержит биржевой курс, то структура данной страницы может сколько угодно изменяться, не приводя к отказу запроса.

Листинг 5. Выражение XQuery для извлечения биржевых котировок из Yahoo! Finance
<table>
{
  for $d in //td
  where contains($d/text()[1], "Last Trade")
  return <tr><td> { data($d/following-sibling::td) } </td></tr>
}
</table>

Погода

Давайте попробуем поработать с другой страницей. На странице Yahoo! Weather содержится несколько панелей портлетов, и я хочу извлечь названия, температуры и значки перечисленных городов. (Страница Yahoo! Weather, http://weather.yahoo.com, показывает погоду в городах, указанных в Вашем My Yahoo!, если Вы зарегистрированы на Yahoo!, а если нет, то предоставляется подборка самых крупных городов). В листинге 6 показан запрос, который ищет подпанель, содержащую текст "New York, NY", а затем перемещается к объемлющей таблице и выбирает все строки:

Листинг 6. XQuery выражение для извлечения информации о погоде из страницы Yahoo! Weather
<table>
{
  for $d in //td[contains(a/small/text(), "New York, NY")]
  for $row in $d/parent::tr/parent::table/tr
  where contains($d/a/small/text()[1], "New York")
  return <tr><td>{data($row/td[1])}</td> 
           <td>{data($row/td[2])}</td>              
           <td>{$row/td[3]//img}</td> </tr>
}
</table>

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

Чикаго, Иллинойс 49...63 FОблачно с прояснениями
Лондон, Великобритания 32...41 FЯсно
Нью-Йорк, Нью-Йорк 36...44 FОблачно
Сан-Франциско, Калифорния 52...67 FОблачно с прояснениями

Данный запрос не является таким же надежным, как запрос, показанный в Листинге 5. Предполагается, что текст "Нью-Йорк, Нью-Йорк" будет располагаться внутри маленького (small) элемента (разметка, которая может легко измениться в следующий раз, когда Yahoo! модернизирует свои страницы). Также, "Нью-Йорк, Нью-Йорк" может запросто встретиться несколько раз на странице, посвященной погоде. Тем не менее, эта доля риска может быть уменьшена при затрате дополнительных усилий на разработку запросов; как и в случае со многими опциями разработки, существует оптимальное соотношение между сложностью и надежностью запроса.

Запросы, показанные в Листинге 5 и Листинге 6, не являются единственными способами создания такого типа запросов. Используя более сложные синтаксические структуры XPath, можно в Листинге 6 объединить два оператора for в одно XPath-выражение, а весь Листинг 5 может быть переписан в виде выражения XPath без использования синтаксиса FLWOR. Если Вы являетесь экспертом в использовании XPath, то Вам наверняка покажется, что гораздо легче использовать подход, в большей степени ориентированный на XPath, в то время как специалистам с большим опытом работы с SQL, скорее всего, синтаксис FLWOR покажется более привлекательным.

Инструментальные программные средства

Требуется исключительно небольшое количество кода для реализации выражений XQuery для страниц HTML. Библиотека JTidy может использоваться для очистки HTML-документа и представления его в качестве DOM-объекта (см. Листинг 1). Механизм Saxon XQuery был использован для компиляции и выполнения запроса к DOM-объекту документа. Как показано в Листинге 7, компиляция и выполнение выражения XQuery относительно DOM-представления документа реализуется всего лишь шестью строчками кода:

Листинг 7. Код для компилирования и выполнения выражения XQuery с помощью Saxon
Configuration c = new Configuration();
StaticQueryContext qp = new StaticQueryContext(c);
XQueryExpression xe = qp.compileQuery(query);
DynamicQueryContext dqc = new DynamicQueryContext(c);
dqc.setContextNode(new DocumentWrapper(tidyDOM, url, c));
List result = xe.evaluate(dqc);

Результатом обработки запроса является List of DOM Element (список DOM-элементов), и Вы можете использовать Вашу любимую DOM-технологию управления (ну ладно, наименее нелюбимую Вами DOM-технологию управления) для преобразования результатов запроса в документ.

Существует множество других реализаций XQuery, некоторые из которых бесплатные, а некоторые - коммерческие, в разделе Ресурсы указано, где их можно найти.


Заключение

Хотя XQuery и был спроектирован для осуществления запросов к обширным базам документов, он также является прекрасным инструментом и для преобразования простых документов. Упрощаете ли вы сложные страницы для отображения на маленьких экранах, извлекаете элементы из различных страниц для их объединения в домашний портал, или просто извлекаете данные из Web-страниц, поскольку не существует никакого другого программного способа получения этих данных - XQuery предлагает достаточно легкий способ извлечения необходимых данных из HTML-страниц.

Ресурсы

  • Примите участие в обсуждении материала на форуме.
  • Оригинал статьи: "Screen-scraping with XQuery."
  • Говард Кац (Howard Katz) в публикации Введение в XQuery (developerWorks, июнь 2001 г.) раскрывает основы и историю попыток стандартизации XQuery.
  • Руководство Николаса Чейза (Nicholas Chase) Обработка XML при помощи XML Query (developerWorks, сентябрь 2002 г.) более глубоко рассматривает использование и синтаксис XQuery.
  • Вы можете прочитать о мобильном телефоне Сэма Пуллара (Sam Pullara) в его блоге.
  • Загрузите JTidy из источника SourceForge.
  • Испытайте реализацию XQuery и XSL от Saxon.
  • Вы можете попробовать бесплатную версию сервера Mark Logic - информационную базу данных, позволяющую осуществлять поиск в обширных базах документов при помощи XQuery.
  • Официальные спецификации для XQuery можно скачать с сайта W3C; на этой странице также находится перечень реализаций XQuery.
  • В данном наборе слайдов из Руководства по XQuery приводится много хороших примеров преимуществ и использования XQuery.
  • Для получения дополнительной информации по технологии Java, посетите developerWorks, раздел Java. Вы найдете техническую документацию, практические руководства, обучающие и скачиваемые материалы, информацию о продукте и тому подобное.
  • Для получения дополнительной информации по XML, посетите developerWorks, раздел XML. Так же как и в разделе Java, Вы найдете техническую документацию, практические руководства, обучающие и скачиваемые материалы, информацию о продукте и тому подобное.
  • Самые последние ресурсы, помогающие в освоении программирования на Java, представлены на сайте Новое в технологии Java.
  • Вступите в сообщество developerWorks, участвуя в блогах developerWorks.
  • Просмотрите книги по этой и другим техническим темам.

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=198205
ArticleTitle=Теория и практика Java: Анализ экранных данных с помощью XQuery
publish-date=02272007