加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

万能的XML

发布时间:2020-12-15 23:00:48 所属栏目:百科 来源:网络整理
导读:问题描述: 问题来源于《 Python 基础教程》第三个实例,万能的 xml 。本项目要解决的常见问题是解析(读取和处理) XML 文件。因为使用 XML 几乎能表示任何数据。更具体点来说,通过单个的 XML 文件生成一个完整的网站,这个文件包括站点结果和每个页面的基

问题描述:

问题来源于《Python基础教程》第三个实例,万能的xml。本项目要解决的常见问题是解析(读取和处理)XML文件。因为使用XML几乎能表示任何数据。更具体点来说,通过单个的XML文件生成一个完整的网站,这个文件包括站点结果和每个页面的基本内容。因为对于XML的不了解,所以,先在http://www.w3school.com.cn/x.asp补充了一点XML的背景知识。

首先考虑XML文件需要描述什么:

*网站:不用存储有关网站本身的任何信息,所以,网站就是包括所有文件和目录的顶级元素。

*目录:目录是文件和其他目录的容器。

*页面:一个网页。

*名称:目录和网页都需要名称--当目录和文件出现在文件系统和相应的URL中时,它们可以用作目录名和文件名。

*标题:每个网页都应该有标题(和文件名不同)、

*内容:每个网页都有一些内容。这里只用XHTL来表示内容--这样就能将它传递到最终的网页上,让浏览器对它进行解释。

简单来说,文档由一个包含数个directorypage元素的website元素组成,每个目录元素可以包括更多的页面和目录。Directorypage元素有叫做name的特性,属性值是他们的名字。除此之外,page标签还有title的特性。

用于测试的XML文件表示的简单网站(website.xml

<website>
  <page name="index" title="Home page">
  <h1>Welcome to my Home page</h1>
 
  <p>Hi,there. My name is Mr.gumby,and this is my home page,here are some of my int:</p>
 
  <ul>
    <li><a href="interests/shouting.html">Shouting</a></li>
    <li><a href="interests/sleeping.html">Sleeping</a></li>
    <li><a href="interests/eating.html">Eating</a></li>
  </ul>
  </page>
  <directory name="interests">
    <page name="shouting" title="Shouting">
     <h1>shouting page</h1>
     <p>....</p>
    </page>
    <page name="sleeping" title="Sleeping">
      <h1>sleeping page</h1>
      <p>...</p>
    </page>
    <page name="eating" title="Eating">
       <h1>Eating page</h1>
       <p>....</p>
    </page>
  </directory>
</website>

XML解析:

XML解析是如何工作的,这里所使用的方法叫做SAX,包括编写一组时间处理程序(就如同GUI程序设计),当解析器读XML文档的时候,就可以让它调用这些处理完成解析工作。

使用SAX进行解析时,有很多的事件类型可用,但是本项目只用了三个:元素的起始(开始标签的匹配项)、元素的结束(关闭标签的匹配项)以及纯文本(字符)。要解析XML文件,可以使用xml.sax模块的parse函数。这个函数负责处理程序会作为内容处理程序(contenthandler)对象的方法来实现。需要集成xml.asx.handler中的ContentHandler类,因为它实现了所有需求的事件处理程序(只不过是没有任何效果的未操作),可以再需要的时候覆盖这些函数。

创建HTML页面:

现在已经准备好要创建好原形了,首先忽略目录,专注于创建HTML页面。创建的压面要符合厦门的要求:

1、在每个page元素的开始处,使用给定的文件名打开一个新文件,写入合适的HTML首部,包括给定的标题。

2、在每个page元素的结尾处,写入HTML的页脚,然后关闭文件。

3、在page元素内部时,跳过所有标签和字符,不进行修改(将他们直接写入文件)。

4、不再page元素内部时,忽略所有的标签(比如说website或者directory

大多数的功能实现起来都很简单,但是有两个问题比较不完全清楚:

1、不能简单的“穿过”标签(在建立HTML文件时直接写入文件),因为只有名字(可能还有一些特性)。需要自己重建这些标签(使用尖括号等)。

2、SAX本身无法告诉你当前是否正位于一个page元素内部。所以,需要自己注意这类事情。在这个项目中,程序支队是否穿过标签和文本感兴趣,所以使用一个叫做passthrough的布尔变量在进入和离开页面时进行更新。

初次实现代码如下:

from xml.sax.handler import ContentHandler
from xml.sax import parse
 
class PageMaker(ContentHandler):
    passthrough = False
    def startElement(self,name,attrs):
        if name == 'page':
            self.passthrough = True
            self.out = open(attrs['name']+ '.html','w')
            self.out.write('<html><head>n')
            self.out.write('<title>%s</title>' % attrs['title'])
            self.out.write('</head><body>n')
        elif self.passthrough:
            self.out.write('<' + name)
            for key,val in attrs.items():
                self.out.write(' %s="%s"' % (key,val))
            self.out.write('>')
 
    def endElement(self,name):
        if name =='page':
            self.passthrough = False
            self.out.write('n</body></html>n')
            self.out.close()
        elif self.passthrough:
            self.out.write('</%s>' % name)
    def characters(self,chars):
        if self.passthrough: self.out.write(chars)
 
parse('website.xml',PageMaker())

这段代码运行的结果会得到4HTML文件:

Eating.html

Index.html

Shouting.html

Sleeping.html

其中index.html的截图如下:


再次实现:

因为SAX的机制比较底层且基本,同城会编写一个Mix-in类来处理手机字符数据,管理布尔状态变量(比如passthrough)或是纸牌时间到自己定义的时间中处理程序等这类管理细节。这里主要介绍程序的调度。

主要改变有三点:

1、调度程序的Mix-in

这个类实现了以下几个功能:

A、当使用‘foo’这样的名字调用startElement时,它会试图寻找叫做startFoo的时间处理程序,然后利用给定的特性进行调用。

B、同样的,如果使用‘foo’调用endElement,那么它会试着调用ednFoo

C、如果在这些地方找不到给定的时间处理程序,那么会分别调用defaultStart或者defaultEnd方法。如果连默认的处理程序都没有的话,那就什么都不做。

可能对于抽象编程这类东西了解不够深,对于这个感觉还是有点难以理解。按照书里面说的,所做的方法如下:

A、根据一个前缀(‘start’或‘end’)和一个标签名(比如‘page’)构造处理程序的方法名(比如‘startPage’)

B、使用同一的前缀,构造默认处理程序的名字(比如‘defaultStart’)

C、试着使用getattr获得处理程序,用None作为默认值

D、如果结果可以调用,那么讲一个空元组赋值给args

E、否则试着利用getattr获取默认处理程序,在使用None作为默认值。同样的,将args设定为只包括标签名的元组(以为内默认的处理程序需要)。

F、如果正使用一个起始处理程序,那么将属性添加到参数元组(args)中

G、如果处理程序可调用(或者是可用的具体处理程序,或者是可用的默认处理程序),那么,使用正确的参数进行调用。

2、实现首部、页脚和默认的处理程序

这个比较好理解,程序创建单独的方法用于编写首部和页脚。

3、对目录的支持

为了创建所需要的目录,需要osos.path模块中的一些有用的函数,其中之一就是os.makedirs,它可以再给定的路径中创建所有需要的目录。比如os.makedirs(‘foo/bar/baz’)会在当前的目录中创建foo目录,然后在foo中创建bar目录,最后在bar目录中创建baz,如果foo目录已经存在,那么只会创建barbaz,类似的,如果bar也存在的话那么只有baz会被创建。不过如果baz同样存在的话,就会引发一个异常。

为了避免出现这个异常,需要使用os.path.isdir函数,它可以检查给定的路径是否是目录(即目录是否存在)。另外的一个有用的函数是os.path.join,它可以使用正确的分隔符将数个路径连接起来。

4、事件处理程序

最后需要实现时间处理程序。需要四个对象--两个处理目录,两个处理页面。目录处理程序只有使用directory列表和ensureDirectory方法。

页面处理程序使用writeHeaderwriteFooter方法,初次之外,他们还要设定passthrough变量(穿过XHTML)以及要打开和关闭的页面关联的文件。

网站构建函数如下(website,py):

from xml.sax.handler import ContentHandler
from xml.sax import parse
import os
class Dispatcher:
 
    def dispatch(self,prefix,attrs = None):
        mname = prefix +name.capitalize()
        dname = 'default' +prefix.capitalize()
        method = getattr(self,mname,None)
        if callable(method): args = ()
        else:
            method = getattr(self,dname,None)
            args = name,if prefix == 'start':args +=attrs,if callable(method): method(*args)
 
    def startElement(self,attrs):
        self.dispatch('start',attrs)
 
    def endElement(self,name):
        self.dispatch('end',name)
 
class WebsiteConstructor(Dispatcher,ContentHandler):
    passthrough = False
    def __init__(self,directory):
        self.directory = [directory]
        self.ensureDirectory()
 
    def ensureDirectory(self):
        path = os.path.join(*self.directory)
        if not os.path.isdir(path): os.makedirs(path)
 
    def characters(self,chars):
        if self.passthrough: self.out.write(chars)
 
    def defaultStart(self,attrs):
        if self.passthrough:
            self.out.write('<' + name)
            for key,val in attrs.items():
                self.out.write(' %s = "%s"' %(key,val))
            self.out.write('>')
 
    def defaultEnd(self,name):
        if self.passthrough:
            self.out.write('</%s>' % name)
 
    def startDirectory(self,attrs):
        self.directory.append(attrs['name'])
        self.ensureDirectory()
 
    def endDirectory(self):
        self.directory.pop()
 
    def startPage(self,attrs):
        filename = os.path.join(*self.directory+[attrs['name']+'.html'])
        self.out = open(filename,'w')
        self.writeHeader(attrs['title'])
        self.passthrough = True
 
    def endPage(self):
        self.passthrough = False
        self.writeFooter()
        self.out.close()
 
    def writeHeader(self,title):
        self.out.write('<html>n <head>n   <title>')
        self.out.write(title)
        self.out.write('</title>n </head>n   <body>n')
 
    def writeFooter(self):
        self.out.write('n </body>n</html>n')
 
parse('website.xml',WebsiteConstructor('public_html'))
 

实现结果是生成public_html/的文件夹,这个文件夹里面含有一个index.html的文件和interests的文件夹,interests里面放的是三个HTML文件,HTML的文件实现结果和之前一样。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读