前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >万能的XML(2):再次实现

万能的XML(2):再次实现

作者头像
不可言诉的深渊
发布2019-07-26 16:43:03
5280
发布2019-07-26 16:43:03
举报

万能的XML(1):初次实现

5.再次实现

鉴于SAX机制低级而简单,编写一个混合类来处理管理性细节通常很有帮助。这些管理性细节包括收集字符数据,管理布尔状态变量(如passthrough),将事件分派给自定义事件处理程序,等等。就这个项目而言,状态和数据处理非常简单,因此这里将专注于事件分派。

5.1.分派器混合类

与其在标准通用事件处理程序(如startElement)中编写长长的if语句,不如只编写自定义的具体事件处理程序(如start_page)并让它们自动被调用。你可以在一个混合类中实现这种功能,但通过继承这个混合类和ContentHandler来创建一个子类。


注意 混合类的功能有限,旨在与其它重要的类一起用作父类。


你希望程序具有如下功能。

  • startElement被调用时,如果参数name为'foo',它应尝试查找处理事件start_foo,并提供给它属性调用这个处理程序。
  • 同样endElement被调用时,如果参数name为'foo',它应尝试调用end_foo。
  • 如果没有找到相应的处理程序,这些方法应调用方法default_start或default_end。如果没有这些默认处理程序,就什么都不做。

再来说一下参数的问题。自定义处理程序(如start_foo)无需将标签名作为参数,而自定义默认处理程序(如default_start)需要这样做。另外,只有起始处理程序需要将属性作为参数。

一头雾水?先来编写这个类最简单的部分。

这里实现了事件的基本处理程序,他们只是调用方法dispatch,而dispatch将负责查找合适的处理程序,创建参数元素并使用这些参数调用处理程序。方法dispatch代码如下:

这个方法所做的工作如下。

(1)根据前缀('start'或'end')和标签名(如'page'),生成处理程序名称(如'start_page')。

(2)根据前缀生成默认处理程序的名称(如'default_start')。

(3)尝试使用getattr获取处理程序,并将默认值设置为None。

(4)如果结果是可调用的,就将args设置为一个空元组。

(5)否则,就尝试使用getattr获取默认处理程序,并将默认值也设置为None。另外,将args设置为一个只包含标签名的元组(因为默认处理程序只需要标签名)。

(6)如果要调用的是起始处理程序,就将属性添加到参数元组(args)中。

(7)如果获得的处理程序是可调用的(即为可行的具体处理程序或默认处理程序),就使用正确的参数调用它。

明白了吗?这大致意味着你现在可以像下面这样编写内容处理程序:

鉴于这个分派器混合类负责完成了大部分管理工作,因此内容处理程序非常简单、易于理解。当然,稍后我们将再添加一些功能。

5.2.将首部和尾部写入文件的方法以及默认处理程序

本节比前一节容易得多。我们将编写专门用于将首部和尾部写入文件的方法,而不在事件处理中直接调用self.out.write。这样就可以通过继承来轻松得重写这些方法。我们让将首部和尾部写入文件的方法尽可能简单。

在初次实现中,处理XHTML内容的代码还与处理程序耦合得太紧,现在它们将由default_start和default_end处理。

这些代码与前面相同,只是移到了独立的方法中。(这通常是件好事。)现在就余下最后一块拼图了。

5.3.支持目录

为创建必要的目录,需要使用函数os.makedirs,它在指定的路径中创建必要的目录。例如os.makedirs('foo/bar/baz')在当前目录下创建目录foo,再在目录foo下创建目录bar,然后在目录bar下创建目录baz。如果目录foo已经存在,将只创建目录bar和baz。同样,如果目录bar也已经存在,将只创建目录baz。然而,如果目录baz也已经存在,通常将引发异常。为避免出现这种情况,我们将关键字参数exist_ok设置为True。另一个很有用的函数是os.path.join,它使用了正确的分隔符(例如,在UNIX中为/)将多条路径合而为一。

在整个处理期间,都把当前目录路径存储在变量directory包含的目录名列表中。进入某个目录时,就将其名称附加到这个列表末尾;而离开这个目录时,就将其名称从目录列表中弹出。你可以定义一个函数,来确保当前目录已创建好。

请注意,将目录列表传递给os.path.join时,我使用了星号运算符*进行了参数拆分。

可通过将网站的根目录(如public_html)传递给构造函数,如下所示:

5.4.事件处理程序

终于要实现事件处理程序了。需要4个事件处理程序,其中两个用于处理目录,另外两个用于处理页面。目录处理程序只使用了列表directory和方法ensure_directory。

页面处理程序使用了方法write_header和write_footer。另外,他们还设置了变量passthrough(以便将XHTML代码直接写入文件),而且打开和关闭与页面相关的文件(这可能是最重要的)。

start_page的第一行代码看起来有点吓人,但与ensure_directory的第一行代码大致相同,只是加上了文件名(和扩展名.html)。

这个程序完整的源代码如图所示。

运行之后将生成如下文件和目录:

  • public_html/
  • public_html/index.html
  • public_html/interests/
  • public_html/interests/shouting.html
  • public_html/interests/sleeping.html
  • public_html/interests/eating.html

6.进一步探索

至此,你创建了一个基本程序,可以对其做哪些扩展呢?下面是一些建议。

  • 创建一个新的ContentHandler,用于创建由链接组成的网站目录或菜单。
  • 在网站中添加导航帮助,让用户知道自己身在何处(在哪个目录中)。
  • 创建一个WebsiteConstructor的子类,并在其中重写方法write_header和write_footer,以实现自定义设计。
  • 再创建一个ContentHandler,使其根据XML文件创建单个网页。
  • 创建一个以某种方式(如RSS)提供网站内容摘要的ContentHandler。
  • 研究其他XML转换工具,尤其是XML转换(XSLT)。
  • 使用ReportLab中的Platypus(http://www.reportlab.org)等工具根据XML文件创建一个或多个PDF文档。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-07-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python机器学习算法说书人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档