`
ammayjxf
  • 浏览: 68089 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论
阅读更多
Java 理论与实践: 用 XQuery 进行屏幕搜集
XQuery 把 HTML 提取和转换变成轻松的工作
 

  文档选项
  打印本页

   将此页作为电子邮件发送

  讨论




级别: 初级

Brian Goetz (brian@quiotix.com), 首席咨询师, Quiotix


2005 年 4 月 07 日

XQuery 是用来从 XML 文档中提取信息的 W3C 标准,目前包括 14 个工作草案。虽然 XQuery 主要集中于查找大型半结构化文档数据,但是令人惊讶的是 XQuery 对于一些更平凡的应用也非常有效。在本月的 Java 理论与实践 中,专栏作家 Brian Goetz 将介绍如何有效地把 XQuery 用作 HTML 的屏幕搜集引擎。请在文本附带的 讨论论坛中与作者及其他读者分享您对本文的看法。(可以选择文章顶部或底部的 讨论 来访问这个讨论论坛。)
上个月,Java 技术讲师 Sam Pullara 向我演示了他最新的支持 Java 的电话 Nokia 6630。这个手机使用了全面的技术 —— 嵌入式 JVM、GPRS 和蓝牙,但是它也遭遇了所有智能手机都苦恼的问题 —— 有限的屏幕实际使用区。有些 Web 站点支持基于手机的浏览器,而且嵌入式浏览器也试图在小小的屏幕上有效地渲染页面,但是,在电话屏幕上查看典型的 Web 页面,就像要把一头大象强行塞进车后座一样(其中的每个参与者都会感到失望,包括您、车和大象)。Sam 构建了一个简单的、优雅的解决方案,从他喜欢的 Web 站点上对数据进行屏幕搜集,然后把数据重新格式化,在小屏幕上显示。

新方法

从 HTML 文档提取数据的方法有许多种,但是我真的很喜欢 Sam 采用的方法:既把 XQuery 当作屏幕搜集工具(从页面中提取相当的数据),又把它当作样式表工具(重新格式化数据,以便数据适应页面,不需要进行页面滚动)。只要少量基础设施和一些非常简单的 XQuery 表达式,就可以从大量数据源提取出相关数据 —— 例如交通、天气和财务报价等,并在电话上完好地显示数据。

我过去经常处于这种情况:对 HTML 页面进行屏幕搜集对某些特定问题来说似乎是可行的方案,但是几乎没有用于屏幕搜集的 Java 工具包。有许多 HTML 解析工具,但它们通常缺少足够的抽象能力(把屏幕搜集代码弄得乱七八糟),大量不符合 HTML 规范的应用限制了它们,它们也无法处理那些结构可能随时间发生变化的、动态生成的页面。

为了弥补质量低下的 HTML 和丰富的 XML 处理工具之间的空白,首先要把 HTML 转换成 XML。许多工具有助于完成这项工作;JTidy 工具包做得很好,可以使这项工作变得轻松一些。JTidy 的设计目标是读入典型质量(即很糟)的 HTML 并输出更整洁的结果(有选项可供选择),它还提供了一个 DOM 接口,用来遍历能够发送给 XML 解析器的 HTML 文档。清单 1 中的代码将从 InputStream 中读取 HTML 文档,并生成文档的 DOM 表示:


清单 1. 用 JTidy 把 HTML 转换成 XML 兼容的 DOM

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 文档,但是也可能是已经编入索引并保存在 XML 数据库中的文档集合,甚至是一组关系数据库中的表。像 SQL 一样,XQuery 包含从多个数据集中提取数据、汇总数据、聚合数据和连接数据的函数。

就像 JSP、ASP 或 Velocity 这样的表示性模板语言一样,XQuery 把两个域(表示域和计算域)中的元素组合成一种组合语法。结果,所有 XML 文档都自动成为有效的 XQuery 表达式,并对自身进行评估。XQuery 还包含一些语言语句(language statement),例如“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> 标签)到“代码模式”的切换;然后在 return 子句之后立即进行从“代码模式”到“表示模式”的隐式切换。


清单 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 节点,在该例中,用 XPath 选取了来自 bib.xml 文档的 <book> 节点集,然后进一步过滤出与指定查询参数(出版商是 Addison-Wesley,出版日期是 1991 年之后)匹配的节点。对于选出的每个节点,将在 return 子句中计算表达式,在这里是标记(<li> 标签)与代码(提取出每个 <book> 节点的 <title> 元素的内容)的混合。

这个简单的 XQuery 示例描述了 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 能够做的事情有些感觉,以及提示您如何才能用 XQuery 把 XML 文档转换成自己选择的格式。虽然 XQuery 的大部分功能主要用于查询大型文档或者其他数据源,但是也可以使用 XQuery 非常简单的子集来对 HTML 文档进行屏幕搜集,为各种应用程序提取出需要的数据,例如在屏幕大小有限的设备(例如蜂窝电话)上显示有关的数据,或者创建一个 DIY 的门户网站,聚集并显示来自多个站点的数据。






回页首




用 XQuery 进行屏幕搜集

对 Web 页面做屏幕搜集的许多挑战之一是:它们通常没有可以自我标识的结构,而且它们的结构可能随着站点内容的编辑而变化,甚至有可能根据不同的请求,在页面中插入不同的动态内容(例如广告内容)。因此,对于页面中哪一部分的内容与要提取的数据相对应,通常不得不进行猜测。

股票价格

现在,让我们从提取 Yahoo! 财经页面中 IBM 股票的当前价格开始(http://finance.yahoo.com/q?s=IBM)。这个页面上有许多材料 —— 新闻标题、广告、财经数据,等等,但是我想要的是股票的价格数据,它放在一个表格单元格中,靠近包含“Last Trade”的单元格。清单 5 中的查询语句将选择所有文本内容中包含“Last Trade”的 <td> 节点,然后为每个节点(希望只有一个)输出一个包含后续 <td> 节点内容的表格行。内容是用 return 子句中的 data() 函数提取的;否则,不仅仅会得到 <td> 节点中的文本,还会得到所有的标记。(在这个查询中,惟一包含技巧的部分是 text()[1] 这个部分;在这里,text() 函数匹配的是 <td> 元素中的所有元素 —— 在这个例子只有一个元素,但 XQuery 并不知道这一点 —— 所以必须进一步告诉它在进行文本匹配之前,必须选择第一个文本节点)。只要页面包含一个表格单元格的文本是“Last Trade”,而且后续的单元格包含的是股票价格,那么,即使页面的结构随意变化,也不会造成查询失败。


清单 5. 从 Yahoo! 财经提取股票报价的 XQuery 表达式

<table>
{
  for $d in //td
  where contains($d/text()[1], "Last Trade")
  return <tr><td> { data($d/following-sibling::td) } </td></tr>
}
</table>



天气

现在来试一下另外一个页面。Yahoo! 天气页面包含许多 portlet 面板,我想提取上面所列城市的名称、温度和图标。(如果登录 Yahoo! 天气页面 http://weather.yahoo.com,则屏幕上会显示出在“我的 Yahoo!”中所选城市的天气,否则会显示一些主要大城市的天气情况。)清单 6 显示了一个查询,它查找包含文本“New York, NY”的子面板,然后导航到封闭表格(enclosing table),并选中所有行:


清单 6. 从 Yahoo! 天气提取天气信息的 XQuery 表达式

<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>



然后,对于每一行,XQuery 会提取出三个相关的数据列 —— 城市名称、温度和图标 —— 并输出一个相对简单的表,表中只包含这三项信息。结果就是比较紧凑地显示了所关心城市的信息,适合在小屏幕上显示。结果如下所示:

Chicago, IL  49...63 F  
London, UK  32...41 F  
New York, NY  36...44 F  
San Francisco, CA  52...67 F  

这个查询不像 清单 5 中的查询那么功能强大。它假设文本“New York, NY”将在 small 元素中(这就是下次 Yahoo! 重新设计他们的页面时可以轻松更改的那类标记)。而且,也很容易将“New York, NY”多次显示在天气页面上。但是,可以多花些精力来开发查询,从而减轻这些风险元素;正如许多开发选择一样,在查询的复杂性与查询的稳定性之间会有一个权衡。

清单 5 和 清单 6 中所示的查询不是制作这些查询的惟一方式。使用更复杂的 XPath 语法,清单 6 中的两个 for 子句可以合并到一个 XPath 表达式中,那么整个 清单 5 就能变成一个 XPath 表达式,而不是使用 FLWOR 的语法。如果是 XPath 高手,那么可能会发现,使用更加面向 XPath 的方式会更容易一些,而有更多 SQL 经验的人则会发现 FLWOR 的语法更有吸引力。

工具

针对 HTML 页面执行 XQuery 表达式所需要的代码非常少。可以用 JTidy 库来清理 HTML 文档,然后把它表示成 DOM 对象(请参阅 清单 1)。Saxon XQuery 引擎被用来编译和执行针对文档 DOM 对象的查询。编译和执行一个针对文档 DOM 表示的 XQuery 查询只需要 6 行代码,如清单 7 所示:


清单 7. 用 Saxon 编译和执行 XQuery 表达式的代码

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);



查询计算的结果是 DOM Element 的 List,您可以用自己喜欢的 DOM 操纵技术(或者最不喜欢的 DOM 操作技术)把查询结果转变成文档。

还有其他 XQuery 实现可以使用,有些是免费的,有些是商业的 —— 请参阅 参考资料,去一些地方看看。






回页首




结束语

虽然 XQuery 是为了查询大型文档而设计的,但是对于转换简单的文档,它也是一个不错的工具。不管是把复杂的页面简化成在小屏幕上显示的页面,还是从多个页面提取元素,把它们聚合在自己的门户上,或者仅仅是因为无法通过其他编程方式获得数据才从 Web 页面上提取数据,XQuery 都提供了从 HTML 页面搜集所需数据的相对简单的方法。



分享到:
评论
1 楼 zjutxm 2016-08-08  

相关推荐

    《XML》实验(3) -- XQuery

    2、理解和掌握XQuery查询计划的基本结构、各种子句的使用、(递归)函数的声明和使用、嵌套查询的使用,能够熟练地利用集成开发环境编写完成各种查询工作的XQuery查询计划,为在主流关系数据库中使用XQuery进行数据...

    XML - 实验(4) -- XQuery

    《XML》实验任务书 【4】 XQuery [实验目的] 1、学习如何使用XMLSPY集成开发环境编写、执行、以及调试XQuery。 2、理解和掌握XQuery查询计划的基本结构、各种子句的使用、(递归)函数的声明和使用、嵌套查询的使用...

    XQuery数据查询语言

    XQuery数据查询语言[1-1] XQuery数据查询语言[1-2] XQuery数据查询语言[2-1] XQuery数据查询语言[2-2] XQuery数据查询语言[3-1] XQuery数据查询语言[3-2] XQuery数据查询语言[4]

    XQuery AnXMLquerylanguage.pdf

    XQuery AnXMLquerylanguage.pdf

    关于XQUERY的误解及其解答

    解答了一些关于XQUERY的问题,解答了一些关于XQUERY的问题,解答了一些关于XQUERY的问题。

    XML学习之XQuery 教程

    近期刚整理好的,可以帮助你了解Xquery的教程资料。

    XQuery文档教程

    XQuery经典的学习文档,有很多实例供练习参考

    XML关于Xquery的代码

    学习如何使用XMLSPY集成开发环境编写、执行、以及调试XQuery。 理解和掌握XQuery查询计划的基本结构、各种子句的使用、(递归)函数的声明和使用、嵌套查询的使用,能够熟练地利用集成开发环境编写完成各种查询工作...

    文档数据提取Golang包XQuery.zip

    XQuery 是一个 Golang 包,允许从 HTML 或 XML 文档中使用 XPath 提取文档数据。... import ( ...  "github.com/antchfx/xquery/html"  ...}XML 查询方法:Find(Node, string) []NodeFindOne(*Node, ... 标签:XQuery

    XQuery_ XML 查询语言

    XQuery_ XML 查询语言

    xQuery学习资料.

    xQuery学习资料.中文资料,英文手册.包含使用flwor表达式查询和连接等中文pdf文档.

    xquery 计算进出旅客总数

    按照2005-12-24日机场繁忙程度(计算进出旅客总数)列出机场(忽略没有旅客进出的机场)、以及其进出旅客总数(dataQ3.xquery);(此题选做)

    xquery参考手册.pdf

    xquery参考手册 language:english IMB 需要一定的英语阅读能力

    XML查询语言XQuery及其查询优化

    XML 查询语言 XQuery 及其查询优化

    XQUERY的好例子

    用xquery的例子(解析xml)

    XQuery For Java 例子

    实现Java通过XQuery语言查询XML文件的一个简单例子。

    springboot+jsp+xquery实现登录认证

    springboot+jsp+xquery实现登录认证

    XQuery中表达式的研究及实例分析

    XQuery使用技巧,XQuery中表达式的研究及实例分析

    XQuery Tutorial

    • Part I XQuery expressions, types, and laws • Part II XQuery laws and XQuery core • Part III XQuery processing model • Part IV XQuery type system and XML Schema • Part V Type inference and type ...

    basex xquery查询工具

    xquery的查询工具,找了好久才找到的,其它的基本上就只能运行xquery文件,这个有一个查询界面,可以直接查询,很方便的.

Global site tag (gtag.js) - Google Analytics