此前,一 文对比了DOM + XPath + XSLT 和正则表达式应用在提取网页数据信息和屏幕抓取领域的优缺点,重点说明了采用前者的优势,毫无疑问,采用前者编程成本低很多,有大量的可重用的第三方程序 库或者软件模块供集成,而且做出来的数据抽取规则适用力很强。然而,这些优势是付出一定代价换取来的,主要的代价是:完全控制XSLT需要长时间的学习和 练习,下面总结几点掌握XSLT的难点。
一台机器
XSLT功能十分强大,绝大多数信息提取和网页内容格式化转换任务都可以通过一个XSLT指令文件完成。自 动生成的网页数据抓取规则就是一个XSLT文件。XSLT指令文件中,xsl:template语句将很多指令组织成模块,模块化编程是降低编码成本的有 效方法,然而,初学者容易被XSLT的“模块化”所蒙蔽。实际上,XSLT的处理引擎采用了一种很古怪的方式,你可以这样想象:有一台机器,两个供料口, 原料分别是XSLT指令和被转换的文档(我们关心的是HTML网页),一个成品出口,产品是经过转换后的文档(也就是抽取到的数据),机器在不停的转。这 种机器有个特点,任何原料进去以后都是顺序处理的,如果你觉得目标HTML文档经过一遍处理后还需要来一遍,例如,再提取更多信息,在同一个处理会话中是 不可能的,就像一个磁带机,数据是顺序访问的,两种原料都是这样,这是使用XSLT的最大的拦路虎,很容易出错,出错的现象就是认为能够抓取到的数据没有 抓取到。如果脑子中总是有这台机器,就可避免犯这个错误。
复杂结构数据的提取
如果需要抓取的数据是表结构的,上述问题不容易暴露,而且,XSLT指令文件也可以很简单,但是,绝大多数的网页内容是复杂的树状结构,例如, B2B网站上的商品分类,大类下面有子类,而且多级嵌套,这是树状结构,需要XSLT指令执行深挖嵌套的操作,使用下一节讲解的几个“模块化”指令可以很 好的处理这个问题,然而上一节讲解的顺序处理机破坏了“模块化”,实际上从某种程度上讲成了假模块化。手工编写XSLT很容易出错,编码-验证-修改循环 多次,应 运而生,MetaSeeker中的MetaStudio工具的原理很简单,就是生成一个XSLT指令框架,这是一个框架的时代,我们有开发框架(例如, Spring framework)、网站(CMS)框架(例如,Drupal),MetaStudio生成的是XSLT指令框架,符合顺序处理机原则生成一个XSLT 指令框架,屏蔽了烦人的顺序处理机,用户看到的是真正的模块化。详细讲解了框架生成原理,MetaStudio使用FreeFormat技术确保生成的XSLT文件框架能够正确顺序执行,而且使用目标页面中的语义标记(例如,和CSS selector)提高信息提取精度;而展示了怎样将手工编写的XPath表达式和XSLT指令文件片段集成到MetaStudio生成的框架中。
XSLT的模块化指令
XSL指令集中有几个模块化指令:xsl:template, xsl:for-each , xsl:apply-templates, xsl:call-template,另外,加上xsl:if,使用他们可以编写很容易阅读的XSLT文件。
然而,作为初学者,一定要将顺序化机器铭记在心,否则容易被这几个模块化指令所误导。在互联网上随便看看XSLT的讨论,就会发现很多菜鸟向老鸟求救,甚至有些老鸟干脆建议菜鸟避开哪些雷区,例如:, 老鸟说:不要用xsl:for-each,要用xsl:apply-templates。某种程度上说这是对的,然而在某些环境下必须要使用for- each,例如,与xsl:if配合,先判断是否存在节点集,再使用xsl:for-each,而不用xsl:apply-templates,因为不想 XSL引擎自主匹配。在这种情况下就得将下面的两个重点铭记在心。
此XPath即彼XPath?
我们常说,XSLT离不开XPath,在上节讲解的模块化指令中都要用XPath,例如,match规则,test规则和select规则等等,你 会发现很多XPath并不灵光,例如:./p/text(),不能出现在xsl:template中,此XPath不是彼XPath?如果脑子中有顺序处 理机这个形象,这一点不难理解,当处理到xsl:apply-templates时,引擎要在当前节点后面找一个匹配的节点或者节点集,“.”运算符是多 余的,只能是p/text(),这个原则适用所有match操作,但是,在xsl:for-each中就不一样了,它是select规则,可以 select当前(.)节点,也可以是following-sibling或者preceding-sibling等,也适用所有的select规则。
上下文节点(context node)和当前节点(current node)
上一节我们已经提到了当前节点,还有一个概念叫上下文节点,顺序处理机是恼人的设计,有些时候必须要回头,实际上回不了头,但是,我们可以先暂停这 台机器,停下来后,预先在没有处理的原料中搜寻搜寻,实际上达到了回头的效果,这就是上下文节点和当前节点的作用。限于篇幅不再赘述,感兴趣可以翻看 XSL规范和书籍,另外要记住的概念是:节点集(node set),这要将这台机器剖开看了,看原料的加工过程,有兴趣自己去研究吧。