基于R语言的自动数据收集:网络抓取和文本挖掘实用指南【1.6】
3.2.4 注释及字符数据
XML的语法提供了一种对内容进行注释的方式
在<!--和-->之间的所有内容都不被当作XML代码的一部分,从而会被解析器所忽略。注释可以用在标签之间或元素内容之内,但不能在元素名或属性名的内部使用。
在数据值中有较多需要转义的元素的情况下,转义字符串的使用会相当冗长累赘。例如,假如下面的字符串需要存放在XML文件中:
在XML代码中,这个例子可能翻译成了
为了避免这种乱七八糟的结果,XML提供了一套阻止内容被翻译的环境。它被称为CDATA,使用的情况如下所示:
所有在CDATA部分里的字符都是原样保留的。注释和CDATA部分的区别是,注释不是文档的一部分:
如果在一个XML文件里编写上面两个片段,并在浏览器中打开它,注释部分或者不会显示出来,或者显示了但不会被标出作为XML的一部分。相比之下,CDATA部分会在树中显示出来。如果删除CDATA标签,就会产生一个错误,因为浏览器无法解析&符号和引号。
你可以自己尝试一下。把前面的代码片段用你的文本编辑器保存为一个XML文件,并在浏览器里打开它。修改XML文件的内容并保存,然后在浏览器里重新加载它。试验进行合法及非法的修改。你可以尝试特殊字符、交叉嵌套的标签,以及禁止使用的元素名。
3.2.5 XML语法总结
总结一下,XML语法包含以下一组规则:
1)XML文档必须有一个根元素。
2)所有元素必须有起始标签并且是闭合的,XML声明除外,它并非实际XML文档的一部分。
3)XML元素必须正确地嵌套。
4)XML属性值必须加引号。
5)标签用字符和数字命名,但不能以数字或“xml”开头。
6)标签名不能包含空格,而且区分大小写。
7)空格字符是保留字。
8)某些字符是非法的,必须替换为元字符。
9)注释要用<!--comment-->的形式加入。
10)不需要解析的内容用<![CDATA[…]]>表示。
[1] 两种现有版本的差异是边缘性的,与我们通常不感兴趣的编码问题相关。[2 ] 要了解更多关于编码的知识,请参阅8.3节。
[3] 要了解更多相关信息,请访问http://www.w3.org/TR/xml/#sec-rmd关于W3C的说明。
3.3 结构良好或合法的XML文档的条件
简而言之,XML文档结构良好的条件是遵循3.2节的所有语法规则。从XML文档中提取信息的技术都依赖于合理编写的语法。如果对某个XML文档的结构有疑问,有几种办法可以检查。例如,在http://www.xmlvalidation.com/网站的XML校验器会检查起始和终止标签的不匹配、属性值是否加引号、是否使用了非法字符等情况,简短地说就是:是否有违反规则的情况。
我们可以区分结构良好和合法的XML。XML文档合法的条件是:
1)结构良好;
2)遵循了文档类型定义(DTD)的规则。
正如我们所见,XML文档的结构可以是任意的:标签名和层次结构的深度都是用户自定义的。不过,通过文档类型定义(DTD)这种方式可以限制其任意性。DTD是一套定义了XML结构、元素命名方式及其应该包含的数据类型的声明。一个关于图3-1的实际例子的DTD如下:
在这个XML文档的变体中,DTD被加入了XML文档并包裹在一个DOCTYPE定义<!DOCTYPE bond_movies[http://www.hzcourse.com/resource/readBook? path=/openresources/teach_ebook/uncompressed/15597/OEBPS/Text/...]>的内部。这种形式成为内部DTD。因为我们以网络抓取为目的,一般不需要编写DTD的能力,所以我们不会讲解该声明的每一个细节,而是针对DTD的形式讲述一些基本知识。对元素的声明如下:
元素的子元素的声明如下:
对于混合内容的声明稍微复杂一些。例如,如果某个元素包含了一个或多个<child1>到<child3>的元素实例或直接解析的字符数据,声明的例子如下:
对属性的声明例子如下所示:
IMPLIED属性值的意思是对应的属性是可选的;REQUIRED就代表该属性是必需的。有好几个在线工具可以根据一个DTD来校验XML文件。只要在搜索引擎里输入“dtd validation”然后在搜索结果里找一个靠前面的就行了。
为什么我们要关心XML文档是否结构良好或合法呢?首先很重要的一点是要知道很多XML文件的标头是带有内部DTD的。总的来说,DTD有多种作用。如果发送者和接收者都能提前知道他们将要发送和接收的内容,数据交换过程就能标准化。作为发送者,你可以检查你自己的XML文件是否合法。作为接收者,也可以检查你接收到的XML数据是否是你或你的程序所期望的类型。
DTD本身只是多种XML模式语言之一。这种模式语言有助于描述和限制XML文档中的结构和内容。另外一种模式语言是XML格式(XSD),它是由W3C制定的。它让用户能利用XML语法定义模式,也具有一些优点,但这些优点对于我们的目标用处不大。XML模式可以大显身手的一个领域是XML扩展,这是3.4节的主题。
3.4 XML扩展与技术
我们已经看到,因为XML是可扩展的,从而是灵活的,所以它相对HTML来说,在网络数据交换方面具有一定优势。不过,灵活性也会带来潜在的不确定性或不一致性问题,如同样的元素名用于不同内容的情况。现在已经有一些相关的扩展和技术,它们可以通过引入标准或提供设定标准的技术等方式来提高XML的可用性。在本节中会介绍这些技术中最重要的几个。
3.4.1 命名空间
考虑下面的两段HTML和XML代码:
这两段代码都在<title>元素里存放了信息。如果这些XML代码是嵌套在HTML代码里的,就会产生混乱。正如我们将要看到的,有很多XML扩展用来存放特定的数据,如地理数据、图像数据或财务数据。所有这些语言其实是带有限定词汇表的XML。当在一个文档中用到了多个这种基于XML的语言的时候,如果某个元素或属性名在多个地方都有赋值,它们就可能会相互混淆。XML命名空间就是用来规避这类问题的。它的思路非常简单:对容易混淆的元素,如果给它们分别加上某个唯一性识别码,它们就变成可分辨的了。就像邮政编码可以区分很多名为Springfield的不同地址,以及区域码可以让不同地区的电话号码不会混淆一样,命名空间能够让元素和属性唯一可识别。
命名空间的实现是很直观的:
在这个例子里,命名空间是在根元素里声明的,它使用了xmlns属性和两个前缀(h和t)。命名空间的名字,也就是命名空间的属性值,通常带有一个指向某个互联网地址的统一资源识别码(URI)。上面例子里的两个URI分别指向的是现有W3C主页的互联网资源和一个虚构的域名funnybooknames.com。当处理命名空间的时候,请记住下列规则:
·命名空间可以在根元素或任何其他元素的起始标签内声明。在后一种情况下,该元素的任何子元素都被看作该命名空间的一部分。
·命名空间的名字不一定必须是可用的URL。解析器永远不会跟踪链接,更不用说URI了。任何其他的字符串都可以用。不过,通常的做法是用URI,这有两个原因:首先,它们是较长而且独一无二的字符串,不太可能会发生重复;其次,实际的URL能引导人类读者看到有关的网页,里面可以给出有关该命名空间的更多信息。[1]
·前缀(prefix)不一定要显示声明,因此声明内容可以是xmlns或xmlns:prefix。如果前缀被去掉了,xmlns就被假定为缺省命名空间,任何没有前缀的元素都会被视为在该缺省命名空间里。如果有前缀,它在声明中就会绑定到一个命名空间。不过,属性永远不会属于缺省命名空间。
3.4.2 XML的扩展
迄今为止,我们吹捧了一番XML的灵活性和扩展能力。不过,标准化在数据交换的场景中也是有其益处的。回想一下浏览器处理HTML的方式。它们“知道”一个表格看上去是什么样子的,标题应该如何设置格式,等等。总体而言,许多数据交换过程都可以标准化,因为发送者
和接收者对于要交换的数据的内容和结构具有共识。
按照这样的逻辑,众多XML语言的扩展被开发出来,它们都把XML经典的开放特性和标准化带来的益处融为一体。从这个意义上说,XML已经成为了一种重要的元语言——它为其他XML标记语言提供了总体架构。XML的多种衍生品都依赖于XML模式来表述它所允许的结构、元素、属性和内容。表3-2列出了一些最流行的XML衍生品。在它们之中的语言有用于地理应用的(如KML或GPX等),也有用于网络订阅及广泛使用的办公文档格式的。你也许会惊讶地发现MS Word大量使用了XML。为了获得对网络上无所不在的XML扩展的基本了解,我们重点讨论两个流行的XML标记语言:RSS和SVG。
3.4.3 示例:RSS
网络用户普遍都会逐渐形成自己的一个常用网页书签的清单。定期查看这些站点上的新内容是件累人的事情。真正简易聚合(Really Simple Syndication,RSS)[2]的问世就是来解决这个问题的——不仅是为了用户,也是为了内容提供者。它的基本思路是这样的:新的网站和博主等内容提供者可以把他们的内容转化为一个可以聚合给任何用户的标准格式。
我们用图3-3来描述RSS的逻辑。博客或新闻网站的作者设置一个RSS文件并保存在Web服务器上,里面包含了一些关于新闻提供者的信息。一旦博客上发布了新内容,这个文件就会更新。这两个任务通常都是用类似于RSS builder的RSS创建程序完成的。文章条目或通知清单通常称为RSS订阅源(RSS feed)或RSS频道(RSS channel),可以位于类似http://www.example.net/feed.rss的位置。它是用XML编写,并遵循RSS格式的规则。在这种XML变体里允许的普通元素在表3-3列出。有一些元素描述了频道,其他的则描述单个条目。如果用户要收集频道,可以通过订阅某个RSS阅读器或Feedly这样的聚合器网站,它们会自动找到给定网站的RSS订阅源并安排相应的内容。这些阅读器会自动更新订阅源,并提供了更多的管理功能。这样,用户就能把他们感兴趣的在线新闻组织起来了。
RSS有好几个版本,当前的是RSS 2.0。RSS语法一直是相当简单的,尤其是对那些熟悉XML的用户来说。它的规则也是很严格的,也就是说,它有一套非常有限的可用元素和清晰的文档结构。考虑一个本书配套的虚构RSS频道的例子,如下所示:
RSS文档的前两行以一个XML和RSS的声明开头。<channel>元素包含了元信息和实际的条目。频道的meta数据块有三个必需的元素: <title>、<description>和<link>。在本例中,还有另一个可选元素<lastBuildDate>,它说明了其中内容在该频道最后一次更新的时间。
content数据块由一组<item>元素组成。一旦有任何新故事、新博客等内容发布出来,一个新的<item>元素就会被加到订阅源里。<item>元素又有三个必需的子元素,它们分别叫作<title>、<description>和<link>。核心内容通常存放在<description>元素里。有时候整个条目都放在这里,有时候则只放开头几行或者摘要。总而言之,RSS语法遵循的是和XML语法相同的一套规则。
花点时间去看看真正的RSS订阅源吧。它们在网上随处可见,带有一个RSS图标作为标记。其中有几个关于R语言的流行新闻和博客平台。例如,你可以看看http://planetr.stderr.org/,它会发布新的R组件(通过Dirk Eddelbuettel的CRANberries博客http://dirk.eddelbuettel.com/cranberries/),以及http://www.r-bloggers.com/,这是一个从R语言博客圈收集内容的元博客平台。
RSS 2.0并非唯一的内容聚合格式。除了它的各种前期版本,另一个流行的标准是Atom,它也是基于XML的,具有和RSS非常相似的语法。要把RSS订阅源抓取到R里,我们可以使用3.5节讲解的XML提取工具。
3.4.4 示例:可缩放矢量图
一个更独特却极其流行的XML扩展是可缩放矢量图(Scalable Vector Graphics,SVG)。SVG用于表达二维矢量图形。它由W3C从1999年开始制定,在2001年首次发布(Dailey 2010)。它的思路是创建一个矢量图形格式,以轻量级和灵活的形式存放图形信息,用于在网络上进行数据交换。
矢量图形格式由基本几何形式组成,如点、曲线、圆、线或多边形,全部都能用数学形式表达。相比之下,光栅图形格式是把图形信息保存为一个像素点组成的光栅,也就是特定颜色的矩形单元。和光栅图形相比,矢量图形可以缩放而不损失任何图形质量,通常也更小巧。因为SVG格式是基于XML的,所以SVG图形可以用普通的文本编辑器进行处理。不过,也有一些能简化工作的SVG编辑器。例如,Inkscape就是一个开源的图形编辑器,它缺省支持SVG并可以在所有常见操作系统上运行。[3]要查看SVG文件,你可以使用常见浏览器的当前版本。
要体验SVG效果的第一印象,图3-4给出了一个小SVG文件的代码。这段代码产生一个R图标的程序化表示,效果如图3-5所示。实际上,如果在浏览器中打开一个包含以上样例代码的SVG文件,我们就可以看到图3-5中的图形。SVG语法不仅仅是类似XML,它实际上就是限定了合法元素和属性的XML。一个SVG文件以普通的XML声明开始。standalone属性表明该文档引用了一个外部文件:在本例子中是第2、3行描述的一个外部DTD。这个DTD存放在www.w3.org中,描述了在当前的SVG 1.1版(截至2014年3月)中哪些元素和属性是合法的。描述该图形的实际SVG代码被包裹在<svg>元素中。它包含了一个命名空间和一个版本属性。
SVG使用一套预先定义的元素和属性来表示图形的部件(即“SVG形状”)。SVG的基本形状包括线(<line>)、矩形(<rect>)、圆(<circle>)、椭圆(<ellipse>)、多边形(<polygon>)、文字(<text>),以及所有形状中最通用的:路径(<path>)。这些元素中的每一个都带有一套属性,用来调整相关对象的某些特性,例如,对角的位置、圆的大小和半径等。元素被放置于一个虚拟的坐标系统中,原点(0,0)的位置是左上角。格式化文本也可以放到图形中。元素的顺序是很重要的。列在后面的元素能覆盖前面的元素,因此元素也可以看作图层。此外,SVG带有一个特效的调色板,如钝化或渐变色等。元素甚至可以有动画效果。一个复杂的SVG图形通常是由相当复杂的SVG代码产生的。这种复杂性往往并不是来自高度层级化的结构——大部分元素通常只是根元素的子元素而已——而是来自于大量的元素及其属性。图3-5的简单图形只由三个元素组成——两个椭圆和一个文本元素。默认情况下,元素都以XML元素语法中的紧凑形式出现:元素通常是空元素,除了属性中给出的信息,元素内部不包含其他数据。
回到这个例子,椭圆的位置由它们的属性cx和cy定义,它们的形状则是由水平和垂直半径rx和ry确定的。颜色和其他效果可以通过style属性中的参数传递。白色椭圆画在灰色椭圆之上,这是因为它在代码中就是排在后面的,这样就产生了甜甜圈的效果。最后,大写字母“R”的形状、颜色、字体和位置都是在<text>元素中定义的。
除了矢量图相对于光栅图的主要优点,SVG还有其他一些特性,使之成为有吸引力的网络图形标准:它可以用任何文本编辑器进行编辑、可以用普通浏览器打开、遵循和基础XML相似的语法、而且是针对范围广泛的应用来制定的。我们已经了解XML是灵活的,但由于其灵活性,它无法被浏览器进一步解释。但这种现象对于SVG这样的XML扩展并不成立。因为SVG的元素和属性集合有清晰的定义,浏览器就可以作出相应的实现,把SVG内容显示为有意义的图形而不只是代码,正如它们可以解释并显示HTML代码一样。在HTML5中,SVG图形甚至可以像如下代码那样简单嵌套:
为什么SVG在自动化数据采集的领域能有用呢?直观的印象是,SVG是一个灵活并且广泛应用的矢量图形格式。不过从数据采集角度看,它并不仅仅如此。在这些图形里的信息——而且往往不止是那些可见的部分——是以文本形式存放的,因此可以进行搜索、划分子集等操作。SVG在网络上日渐流行,并在越来越复杂的任务中使用,如保存地理信息、创建交互地图或把海量信息图形化显示。[4]
从上面两个例子中可以总结的信息是:XML应用在很多不同的领域,很多这种应用中蕴涵了潜在的有用信息。而好消息是:我们会了解用R来查找和处理这些信息是多么容易的一件事,不论这些信息是存放在“纯”XML中还是它的任何扩展中。
[1] 如果同样的URL被反复使用:如http://www.w3.org/1999/xhtml,这就减少了命名空间的效用。因此,应该考虑引用你自己具有完全控制权的站点,如存放了DTD或XML模式的自有网站。
[2 ] 起初,RSS是RDF Site Summary的缩写,后来又被重新定义为Rich Site Summary。在2002年再次被修改为Really Simple Syndication。[3] 要了解更多信息和下载Inkscape,请访问http://inkscape.org/。
[4 ] 要更深入地了解SVG,请查阅Eisenberg(2002)的相关文献以及W3C网页中的介绍:http://www.w3.org/Graphics/SVG/。如果想速成,那么Dailey(2010)编写的《SVG入门》一书应该会有用。
