设为首页 收藏本站
查看: 1039|回复: 0

[经验分享] 可爱的 Python:DOM 的动态性

[复制链接]

尚未签到

发表于 2017-4-29 13:26:00 | 显示全部楼层 |阅读模式

可爱的 Python:DOM 的动态性
  近观 Python 的 xml.dom 模块

DSC0000.gif
DSC0001.gif



文档选项


<noscript>&lt;tr valign="top"&gt;&lt;td width="8"&gt;&lt;img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif"/&gt;&lt;/td&gt;&lt;td width="16"&gt;&lt;img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif"/&gt;&lt;/td&gt;&lt;td class="small" width="122"&gt;&lt;p&gt;&lt;span class="ast"&gt;未显示需要 JavaScript 的文档选项&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;</noscript>
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
DSC0002.gif   将此页作为电子邮件发送


<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas -->
<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->

  级别: 初级
  David Mertz 博士 (mertz@gnosis.cx), 总裁, Gnosis Software, Inc.

  2000 年  7 月  01 日

本文中,David Mertz 更为详细地说明了已在             上一篇专栏文章中讨论过的 Python 的高级 xml.dom模块的使用。以阐明代码样本及解释如何编码加到完整 XML文档处理系统中的若干元素来说明 xml.dom 的工作方法。         

<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->  <!--END RESERVED FOR FUTURE USE INCLUDE FILES-->  什么是 Python?什么是 XML?
  Python 是由 Guido van Rossum 开发的可免费获得的高级解释型语言。其语法简单易懂,而其面向对象的语义功能强大,却又灵活随意。Python 几乎适用于每一种计算机平台,并且在平台间具有很强的可移植性。
  XML 是“标准通用标记语言”(SGML) 的一种简化版本。通过一种特殊的文档类型 HTML,您也许非常熟悉 SGML。XML 文档与 HTML 一样,都是由散布于文本中的、以尖括号括起的标记确定其结构的文本组成的。但 XML 包含了许多系统标记,它们使 XML 可以用于多种用途:杂志文章和用户文档、结构化数据的文件(如 CSV 和 EDI 文件)、程序间中间通信的消息、建筑图纸(如 CAD 格式)以及许多其它用途。可以创建一组标记,以生成任何一种您想要表达的结构化信息,这就是为什么 XML 会逐渐流行,成为表示各种信息的公共标准。



DSC0003.gif







回页首




  文档对象模型
  xml.dom 模块对于 Python 程序员来说,可能是使用 XML 文档时功能最强大的工具。不幸的是,XML-SIG 提供的文档目前来说还比较少。W3C 语言无关的 DOM 规范填补了这方面的部分空白。但 Python 程序员最好有一个特定于 Python 语言的 DOM 的快速入门指南。本文旨在提供这样一个指南。在                   上一篇专栏文章 中,某些样本中使用了样本 quotations.dtd 文件,并且这些文件可以与本文中的代码样本档案文件一起使用。              
  有必要了解 DOM 的确切含义。这方面,正式解释非常好:

“文档对象模型”是平台无关和语言无关的接口,它允许程序和脚本动态访问和更新文档的内容、结构和样式。可以进一步处理文档,而处理的结果也可以合并到已显示的页面中。(万维网联盟 DOM 工作组)
  DOM 将 XML 文档转换成树 -- 或森林 -- 表示。万维网联盟 (W3C) 规范给出了一个 HTML 表的 DOM 版本作为例子。



  如上图所示,DOM 从一个更加抽象的角度定义了一组可以遍历、修剪、改组、输出和操作树的方法,而这种方法要比 XML 文档的线性表示更为便利。











回页首




  将 HTML 转换成 XML
  有效的 HTML 几乎就是有效的 XML,但又不完全相同。这里有两个主要的差异,XML 标记是区分大小写的,并且所有 XML 标记都需要一个显式的结束符号(作为结束标记,而这对于某些 HTML 标记是可选的;例如:                   <img src="X.png" /> )。使用 xml.dom 的一个简单示例就是使用 HtmlBuilder() 类将 HTML 转换成 XML。              


try_dom1.py

        
        """Convert a valid HTML document to XML
   USAGE: python try_dom1.py < infile.html > outfile.xml
"""






import

         sys



from

         xml.dom



import

         core



from

         xml.dom.html_builder



import

         HtmlBuilder

        # Construct an HtmlBuilder object and feed the data to it
b = HtmlBuilder()
b.feed(sys.stdin.read())

        # Get the newly-constructed document object
doc = b.document

        # Output it as XML






print

         doc.toxml()




  HtmlBuilder() 类很容易实现它继承的部分基本 xml.dom.builder 模板的功能,它的源码值得研究。然而,即使我们自己实现了模板功能,DOM 程序的轮廓还是相似的。在一般情况下,我们将用一些方法构建一个 DOM 实例,然后对该实例进行操作。DOM 实例的 .toxml() 方法是一种生成 DOM 实例的字符串表示的简单方法(在以上的情况中,只要在生成后将它打印出来)。











回页首




  将 Python 对象转换成 XML
  Python 程序员可以通过将任意 Python 对象导出为 XML 实例来实现相当多的功能和通用性。这就允许我们以习惯的方式来处理 Python 对象,并且可以选择最终是否使用实例属性作为生成 XML 中的标记。只需要几行(从 building.py 示例派生出),我们就可以将 Python“原生”对象转换成 DOM 对象,并对包含对象的那些属性执行递归处理。


try_dom2.py

        
        """Build a DOM instance from scratch, write it to XML
   USAGE: python try_dom2.py > outfile.xml
"""






import

         types



from

         xml.dom



import

         core



from

         xml.dom.builder



import

         Builder

        # Recursive function to build DOM instance from Python instance






def







object_convert

        (builder, inst):


        # Put entire object inside an elem w/ same name as the class.
    builder.startElement(inst.__class__.__name__)




for

         attr



in

         inst.__dict__.keys():




if

         attr[0] ==

        '_':      

        # Skip internal attributes


continue


        value = getattr(inst, attr)




if

         type(value) == types.InstanceType:


        # Recursively process subobjects
            object_convert(builder, value)




else

        :


        # Convert anything else to string, put it in an element
            builder.startElement(attr)
            builder.text(str(value))
            builder.endElement(attr)
    builder.endElement(inst.__class__.__name__)



if

         __name__ ==

        '__main__':


        # Create container classes






   class







quotations

        :



pass









   class







quotation

        :



pass
        # Create an instance, fill it with hierarchy of attributes


    inst = quotations()
    inst.title =

        "Quotations file (not quotations.dtd conformant)"
    inst.quot1 = quot1 = quotation()
    quot1.text =

        """'"is not a quine" is not a quine' is a quine"""
    quot1.source =

        "Joshua Shagam, kuro5hin.org"
    inst.quot2 = quot2 = quotation()
    quot2.text =

        "Python is not a democracy. Voting doesn't help. "+\


        "Crying may..."
    quot2.source =

        "Guido van Rossum, comp.lang.python"






         # Create the DOM Builder
    builder = Builder()
    object_convert(builder, inst)




print

         builder.document.toxml()




  函数 object_convert() 有一些限制。例如,不可能用以上的过程生成符合 XML 文档的 quotations.dtd:#PCDATA 文本不能直接放到                   quotation  类中,而只能放到类的属性中(如                   .text )。一个简单的变通方法就是让                   object_convert()  以特殊方式处理一个带有名称的属性,例如                   .PCDATA 。可以用各种方法使对 DOM 的转换变得更巧妙,但该方法的妙处在于我们可以从整个 Python 对象开始,以简明的方式将它们转换成 XML 文档。              
  还应值得注意的是在生成的 XML 文档中,处于同一个级别的元素没有什么明显的顺序关系。例如,在作者的系统中使用特定版本的 Python,源码中定义的第二个 quotation 在输出中却第一个出现。但这种顺序关系在不同的版本和系统之间会改变。Python 对象的属性并不是按固定顺序排列的,因此这种特性就具有意义。对于与数据库系统相关的数据,我们希望它们具有这种特性,但是对于标记为 XML 的文章却显然不希望具有这种特性(除非我们想要更新 William Burroughs 的 "cut-up" 方法)。











回页首




  将 XML 文档转换成 Python 对象
  从 XML 文档生成 Python 对象就像其逆向过程一样简单。在多数情况下,用 xml.dom 方法就可以了。但在某些情况下,最好使用与处理所有“类属”Python 对象相同的技术来处理从 XML 文档生成的对象。例如,在以下的代码中,函数                   pyobj_printer() 也许是已经用来处理任意 Python 对象的函数。              


try_dom3.py

        
        """Read in a DOM instance, convert it to a Python object
"""






from

         xml.dom.utils



import

         FileReader



class







PyObject

        :



pass
def







pyobj_printer

        (py_obj, level=0):


        """Return a "deep" string description of a Python object"""




from



         string



import

         join, split




import

         types
    descript =

        ''




for



         membname



in

         dir(py_obj):
        member = getattr(py_obj,membname)




if

         type(member) == types.InstanceType:
            descript = descript + (

        ' '*level) +

        '{'+membname+

        '}\n'
            descript = descript + pyobj_printer(member, level+3)




elif

         type(member) == types.ListType:
            descript = descript + (

        ' '*level) +

        '['+membname+

        ']\n'




for



         i



in

         range(len(member)):
                descript = descript+(

        ' '*level)+str(i+1)+

        ': '+ \
                           pyobj_printer(member,level+3)




else

        :
            descript = descript + membname+

        '='
            descript = descript + join(split(str(member)[:50]))+

        '...\n'




return



         descript



def







pyobj_from_dom

        (dom_node):


        """Converts a DOM tree to a "native" Python object"""
    py_obj = PyObject()
    py_obj.PCDATA =

        ''




for



         node



in

         dom_node.get_childNodes():




if

         node.name ==

        '#text':
            py_obj.PCDATA = py_obj.PCDATA + node.value




elif

         hasattr(py_obj, node.name):
            getattr(py_obj, node.name).append(pyobj_from_dom(node))




else

        :
            setattr(py_obj, node.name, [pyobj_from_dom(node)])




return

         py_obj

        # Main test
dom_obj = FileReader(

        "quotes.xml").document
py_obj = pyobj_from_dom(dom_obj)



if

         __name__ ==

        "__main__":




print

         pyobj_printer(py_obj)




  这里的关注焦点应该是函数                   pyobj_from_dom() ,特别是起实际作用的 xml.dom 方法                   .get_childNodes() 。在                   pyobj_from_dom()  中,我们直接抽取标记之间的所有文本,将它放到保留属性                   .PCDATA  中。对于任何遇到的嵌套标记,我们创建一个新属性,其名称与标记匹配,并将一个列表分配给该属性,这样就可以潜在地包含在在父代块中多次出现的标记。当然,使用列表要维护在 XML 文档中遇到的标记的顺序。              
  除了使用旧的                   pyobj_printer()  类属函数(或者,更复杂和健壮的函数)之外,我们可以使用正常的属性记号来访问                   py_obj 的元素。              


Python 交互式会话

>>>



from

         try_dom3



import

         *
>>> py_obj.quotations[0].quotation[3].source[0].PCDATA

        'Guido van Rossum, '














回页首




  重新安排 DOM 树
  DOM 的一大优点是它可以让程序员以非线性方式对 XML 文档进行操作。由相匹配的开/关标记括起的每一块都只是 DOM 树中的一个“节点”。当以类似于列表的方式维护节点以保留顺序信息时,则顺序并没有什么特殊之处,也并非不可改变。我们可以轻易地剪下某个节点,嫁接到 DOM 树的另一个位置(如果 DTD 允许,甚至嫁接到另一层上)。或者添加新的节点、删除现有节点,等等。


try_dom4.py

        
        """Manipulate the arrangement of nodes in a DOM object
"""






from

         try_dom3



import

         *

        #-- Var 'doc' will hold the single <quotations> "trunk"
doc = dom_obj.get_childNodes()[0]

        #-- Pull off all the nodes into a Python list
# (each node is a <quotation> block, or a whitespace text node)
nodes = []



while

         1:




try

        : node = doc.removeChild(doc.get_childNodes()[0])




except

        :



break


    nodes.append(node)

        #-- Reverse the order of the quotations using a list method
# (we could also perform more complicated operations on the list:
# delete elements, add new ones, sort on complex criteria, etc.)
nodes.reverse()

        #-- Fill 'doc' back up with our rearranged nodes






for

         node



in

         nodes:


        # if second arg is None, insert is to end of list
    doc.insertBefore(node, None)

        #-- Output the manipulated DOM






print

         dom_obj.toxml()




  如果我们将 XML 文档只看作一个文本文件,或者使用一个面向序列的模块(如 xmllib 或 xml.sax),那么在以上几行中执行对 quotation 节点的重新安排操作将引出一个值得考虑的问题。然而如果使用 DOM,则问题就如同对 Python 列表执行的任何其它操作一样简单。



  参考资料


  • 您可以参阅本文在 developerWorks 全球站点上的                      英文原文.
  • 下载                      本文中所提到的文件和列表 的 zip 压缩文件。
  • 阅读 David 的前一篇文章,                      可爱的 Python:将 XML 和 Python 结合起来 ,以获取与 XML 相关的 Python 模型的介绍,这样就可以选择所需的模型。
  • 请仔细阅读                      Python Special Interest Group on XML ,它主要讨论用于开发、改进或维护特定 Python 资源的合作成果。在 Python 网站上,每个 SIG 都有章程、协调员、邮件列表和目录。
  •                       The World Wide Web Consortium's DOM page是 DOM 信息、新闻和新发行版的主页
  • 请浏览                      The DOM Level 1 Specification。



  关于作者

  迄今为止,David Mertz 已经编写了几十年的软件,同时也花了大致相同的时间撰写了                   关于 不同问题的文章。然而殊途同归。Python 吸引他的主要原因是与其它编程语言相比,Python 允许语句中断以及省略非重读首字母。可以只写出含义,而不必输入额外的语言。可以通过                  mertz@gnosis.cx 与 David Mertz 取得联系;                   http://gnosis.cx/publish/ 上刊登了他写的文章。非常欢迎对过去的、这一篇或将来的专栏文章提出意见和建议。              




运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-370819-1-1.html 上篇帖子: 用C语音编写python的扩展模块,也就是python调c库 下篇帖子: 一个Python程序员的进化[转]
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表