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