(转自:http://www.ibm.com/developerworks/cn/xml/x-wbdm/)

 

2001 年 6 月 01 日

不可否认,万维网是到目前为止世界上最丰富和最密集的信息来源。但是,它的结构使它很难用系统的方法来利用信息。本文描述的方法和工具将使那些熟悉 Web 最常用技术的开发人员能快速而便捷地获取他们所需的以 Web 方式发布的信息。

在信息时代快速成长起来的万维网导致各种各样的公用信息被大量分发。 不幸的是,尽管作为信息主要载体的 HTML 提供了一种方便地向读者呈现信息的方法, 但它可能并不是一个很好的可以从中自动抽取与数据驱动的服务或应用程序相关的信息的结构。

已经尝试了多种方法来解决这个问题。大多数方法都采用一些专用查询语言的形式 把 HTML 页面的各个部分映射成代码,而这些代码将 Web 页面上的信息填入到数据库 中。尽管这些方法可能提供一些好处,但大部分会因为以下两个原因变得不切实际:首先,它们需要开发人员花时间去学习一种无法在其它情况下使用的查询语言,其次,它们还不够健壮到能处理不可避免的对目标 Web 页面的简单更改。

在本文中,将讨论使用标准 Web 技术―― HTML、XML 和 Java ――开发的一种 基于 Web 的数据挖掘方法。这种方法即使不比其它专用方法更强大,也和其 它方法不相上下,并且对于那些已经熟悉 Web 技术的人来说,只需要付出很少 的努力就可以收到很好的效果。另外,本文还附送了许多开始数据抽取所需的代码。

HTML:优点与缺点

HTML 通常是一个很难用程序手段处理的媒体。Web 页面中的大多数内容描述与数据驱动 的系统无关的格式编排,并且,由于要动态添加标题以及编写其它服务器端脚本,所以文档 结构可能在每次连接到页面时都需要进行更改。又因为所有 Web 页面主要部分的格式编排 不合理,所以使问题变得更为复杂,其结果是现在的 Web 浏览器在进行 HTML 语法分析 时非常不严谨。

尽管存在这些问题,但是 HTML 在数据挖掘方面仍然具有优势。您所 感兴趣的数据通常可以用 HTML 树中深度嵌套的单 个 <table><div> 标记隔离开来。 这使得抽取过程可以专门在文档的一小部分内执行。在缺少客户机端脚本 的情况下,只有一种定义下拉菜单和其它数据列表的方法。HTML 的这些方面允许 我们在一旦拥有可用格式的数据时能集中精力于数据抽取。





回页首


背景技术

这里描述的数据挖掘技术的关键是把现有的 Web 页面转换成 XML,或转 换成 XHTML 可能更适当,并使用众多工具中的一小部分来处理 XML 结构的数据, 以检索出适当的数据。

幸好有一个解决方案可以改正 HTML 页面设计的薄弱之处。Tidy(可以从一些编程语言中获取的库)是一个免费使用的产品,可用于改正 HTML 文档中的常见错误并生成格式编排良好的等价文档。还可以使用 Tidy 来 生成 XHTML(XML 的子集)格式的这些文档。(请参阅 参考资料)。

本文中的代码示例是用 Java 编写的,并且在编译和运行它们时,需要在您系统 的 classpath 中存在 Tidy jar 文 件。它们还需要通过 Apache 项目、Xerces 和 Xalan 使 XML 库可 用。这两个库都基于 IBM 提供的代码并分别控制 XML 语法分析 和 XSL 变换。这三种库中的每一个都可从 Web 上免费获取,要找到 它们,可以追随上述链接或参考本文后面的参考资料。理解 Java 编程 语言、XML 和 XSL 变换将对您理解以下示例有帮助。有关这些技术的 参考资料,可以在本文后面找到。





回页首


方法概述和示例简介

我们用示例的 方式来介绍数据抽取的方法。假设我们有兴趣跟踪几个月以来每天不同时间测得 的华盛顿州西雅图的温度和湿度级别。假如,没有现成的软件用于 报告此类信息以满足我们的需求,我们仍然拥有从众多公共网站收集此类信息的机会。

图 1举例说明了抽取的全过程。Web 页面 只有在创建了一个可合并到现有数据集的数据集后才会被检索和处理。


图 1. 概要说明抽取过程
图 1. 概要说明抽取过程

只需要很少的几个步骤,我们就可以拥有一个收集我们信息的合适而可靠的系 统。这里列出这些步骤是为了提供该过程的简要概述,图 1 中显示的是这一过程 的较高形式。

  1. 标识数据源并把它映射成 XHTML。
  2. 查找数据内的引用点。
  3. 将数据映射成 XML。
  4. 合并结果并处理数据。

这些步骤中的每一步都将加以详细说明,并将提供执行它们所必需的代码。





回页首


获取信 XHTML 格式的源信息

为了抽取数据,当然需要知道可以在哪里找到它。在大多数情况下,源信息是 显而易见的。如果要从 developerWorks 收集文章的标题和 URL,我们 将使用 http://www.ibm.com/developerworks/?S_TACT=105AGX52&S_CMP=cn-a-x作为我们的目标。在天气这个例子中,我们有若干可供选择的信息源。我们将 在示例中使用 Yahoo!Weather,但使用其它信息源也具有同等效果。 我们将特别跟踪 URL: http://weather.yahoo.com/forecast/Seattle_WA_US_f.html上 的数据。 图 2显示了这个页面的屏幕快照。


图 2. 华盛顿州西雅图的 Yahoo! Weather Web 页面
图 2. 华 盛顿州西雅图的 Yahoo! Weather Web 页面

在考虑信息源时,牢记以下这些要素非常重要:

  • 信息源是否是在可靠的网络连接上生成可靠的数据?
  • 信息源从现在起将存在多久?一个星期、一个月或甚至一年?
  • 信息源的布局结构有多稳定?

我们寻求能够在动态环境下工作的健壮的解决方案的过程中,在抽取可用的最可靠和最稳定的信息 源时,我们的工作将是最简单的。

一旦确定了信息源,我们在抽取过程中的第一步就是将数据 从 HTML 转换成 XML。我们将通过构造名为 XMLHelper (由 static helper 函数 组成)的 Java 类来完成这一任务以及其它与 XML 相关任务。这个类的全部信息源可以 通过以下到 XMLHelper.java XMLHelperException.java 的 链接找到。随着本文的继续展开,我们将构建这个类的方法。

我们使用 Tidy 库提供的函数在 XMLHelper.tidyHTML() 方法中执行 转换。这个方法接受 URL 作为一个参数并返回一个“XML 文档”作 为结果。当调用此方法或任何其它与 XML 相关的方法时,请仔细检查是否有任何异 常。 清单 1中显示了执行这些操作的 代码。 图 3显示了代码结果,Microsoft 的 Internet Explorer XML 查看器使用 Weather 页面中的 XML。

清单 1

 /**
* Retrieve an HTML page, convert the source to XML,
* and write the result to a file.
*/
public static void main(String args[]) {
  try {
    Document doc = XMLHelper.tidyHTML("http://weather.yahoo.com/forecast/Seattle_WA_US_f.html");
XMLHelper.outputXMLToFile(doc, "XML" + File.separator + "weather.xml");
  } catch (XMLHelperException xmle) {
    // ... Do Something ...
  }
}

 

 

 


图 3. 转换成 XHTML 的 Yahoo! Weather Web 页面
图 3. 转 换成 XHTML 的 Yahoo! Weather Web 页面




回页首


查找数据的引用点

请注意,无论是在 Web 页面还是源 XHTML 视图中的绝大多数的信息都与我们完 全无关。我们接下来的一个任务是在 XML 树中找出一个特定区域,我们可从中 抽取我们的数据而无需关心外来信息。对于更复杂的抽取, 我们可能需要在单个页面上找出这些区域的若干实例。

完成这一任务的最简单的办法通常是,首先检查 Web 页面,然后使用 XML。只需 要看一下页面,就可以知道我们正在查找的信息位于页面的中上部区域中。即使对 HTML 的熟悉程度 非常有限,也很容易推断出我们正在查找的数据可能都包含在同一 个 <table> 元素下,并且这个表可能总是包含 象“Appar Temp”和“Dewpoint”这样的字,无论当天的数据可能是什么。

记下我们观察到的内容,现在要考虑页面所生成的 XHTML。搜 索“Appar Temp”的文本(如 图 4所示) 说明该文本确实在一个包含我们所需的所有数据的表中。我们将把 该表作为引用点或锚。


图 4:通过查找包含文本“Appar Temp”的表找到锚
图 4:通 过查找包含文本“Appar Temp”的表找到锚

现在,我们需要找到这个锚的方法。因为我们正准备使用 XSL 来转换我们 的 XML,所以可以使用 XPath 表达式来完成这个任务。我们将使用以下这个普通的表达式:

/html/body/center/table[6]/tr[2]/td[2]/table[2]/tr/td/table[6]

这个表达式指定了从根 <html> 元素到锚的 路径。这个普通的方法将导致我们对页面布局的修改非常容易遭到 破坏。较好的方法是根据周围的内容指定锚。通过使用这个方法,我们把 XPath 表达式重新 构造成:

//table[starts-with(tr/td/font/b,'Appar Temp')]

...更好一些,可以利用 XSL 将 XML 树转换成字符串的方法:

//table[starts-with(normalize-space(.), 'Appar Temp')]





回页首


将数据映射成 XML

拥有这个锚,我们可以创建实际抽取数据的代码。这个代码将以 XSL 文件的形式 出现。XSL 文件的目的是标识锚,指定如何从锚获取我们正在查 找的数据(以简短跳跃的方式),并且用我们所需的格式构造一个 XML 输 出文件。这个过程实际上比想象的要简单得多。 清单 2给出 了将执行这个过程的 XSL 代码,这些代码还可 以作为一个 XSL 文本文件获取。

<xsl:output> 元素仅告诉处理器我们希望的的变换结果 是 XML。第一个, <xsl:template> 建立名 为 <xsl:apply-templates> 的根元素以搜索锚。第二 个, <xsl:template> 让我们只匹配需要 匹配的内容。最后那个, <xsl:template>match 属性 中定义锚,然后告诉处理器跳到我们尝试挖掘的温度与湿度数据。

当然,只编写 XSL,作业将不会完成。我们还需要一个执行转换的工具。因此, 我们利用 XMLHelper 类方法对 XSL 进行语法分析并执行这个转 换。执行这些任务的方法分别名为 parseXMLFromURL()transformXML() 。清单 3 给出了使用这些方法的 代码。

清单 2

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output version="1.0" indent="yes" encoding="UTF-8" omit-xml-declaration="no" method="xml"/>
  
  <xsl:template match="/html">
    <RESULT>
      <WEATHER>
        <xsl:apply-templates/>
      </WEATHER>
    </RESULT>
  </xsl:template>
  <xsl:template match="text()"></xsl:template>
  <xsl:template match="table[starts-with(normalize-space(.),'Appar Temp')]">
    <TEMPERATURE>
      <xsl:value-of select="tr/td[2]/font"/>
    </TEMPERATURE>
	
    <HUMIDITY>
      <xsl:value-of select="tr[3]/td[2]/font"/>
    </HUMIDITY>
  </xsl:template>
</xsl:stylesheet>

 

 

 


清单 3

/**
 * Retrieve the XHTML file written to disk in the Listing 1
 * and apply our XSL Transformation to it. Write the result
 * to disk as XML.
 */
public static void main(String args[]) {
  try {
    Document xhtml = XMLHelper.parseXMLFromURLString("file://weather.xml");
    Document xsl   = XMLHelper.parseXMLFromURLString("file://XSL/weather.xsl");
    Document xml   = XMLHelper.transformXML(xhtml, xsl);
    XMLHelper.outputXMLToFile("XML" + File.separator + "result.xml");
  } catch (XMLHelperException xmle) {
    // ... Do Something ...
  }
}





回页首


合并与处理结果

如果我们只执行一次数据抽取,我们现在已经完成了。 但是,我们并不只是想知道某一时刻的温度,而是要知道若干不同时刻 的温度。现在,我们需要做的是反复执行抽取过程,把结果合并到 单个 XML 数据文件中。我们本来可以再次使用 XSL 执行,但我们将为 把 XSL 文件合并到 XMLHelper 类中最后创建一个方法。 mergeXML() 方法允许我们把在当前抽取中获得的数据合并到 包含以前抽取数据的档案文件中。

WeatherExtractor.java 文件 中给出了用于运行整个过程的代码。我把程序执行调度任务留给读者, 因为执行这些任务的与系统相关的方法通常比简单的编程方法高级。 图 5 显示 了每天运行一次 WeatherExtractor ,共运行四天的结果。


图 5. Web 抽取的结果
图 5. Web 抽取的结果




回页首


结束语

在本文中,我们已经描述并证明从目前存在的最大信息来源――万维网――抽取 信息的强壮方法的基本原则。我们还讨论了能够使任何 Java 开发人员花最少的精力和 具备最少的抽取经验就可以开始他们自己抽取工作所必需的编码工具。尽管本文中的示例仅 集中于抽取有关华盛顿,西雅图天气的信息,但是这里出现的所有代码几乎 都可以在任何数据抽取中重复使用。事实上,除 对 WeatherExtractor 类进行少许更改外, 需要为其它数据挖掘项目进行更改的代码只有 XSL 变换代码(顺便提一下,它从 不需要编译)。

这种方法与想象中的一样简单。通过明智地选择可靠的数据源以及在这些数据源中 选取与内容相关但与格式无关的锚,可以使您拥有一个维护成本低廉,可靠的数据抽取 系统。并且,根据经验级别和要抽取的数据量,您可以在一小时之内就能安装与运行它。



参考资料



作者简介

Jared Jackson

Jared Jackson 自 2000 年 5 月 Harvey Mudd 大学毕业以来一直在 IBM Almaden Research Center 工 作。Jared 还是斯坦福大学“计算机科学”系的研究生。可通 过 mailto:jjared@almaden.ibm.com?cc=jjared@almaden.ibm.com与 Jared 联系。


Jussi Myllymaki

Jussi Myllymaki 于 1999 年作为“研究职员”加盟 IBM Almaden Research Center, 并从麦迪逊的威斯康星大学获得博士学位。可通 过 mailto:jussi@almaden.ibm.com?cc=jussi@almaden.ibm.com与 Jussi 联系。

Posted on 2009-05-31 15:08  hesen  阅读(854)  评论(0编辑  收藏  举报