当前位置:Gxlcms > Python > 详解Python中DOM方法的动态性

详解Python中DOM方法的动态性

时间:2021-07-01 10:21:17 帮助过:87人阅读

文档对象模型

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

有必要了解 DOM 的确切含义。这方面,正式解释非常好:

“文档对象模型”是平台无关和语言无关的接口,它允许程序和脚本动态访问和更新文档的内容、结构和样式。可以进一步处理文档,而处理的结果也可以合并到已显示的页面中。(万维网联盟 DOM 工作组)

DOM 将 XML 文档转换成树 -- 或森林 -- 表示。万维网联盟 (W3C) 规范给出了一个 HTML 表的 DOM 版本作为例子。

2015411151828304.gif (368×205)

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

将 HTML 转换成 XML

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

  1. """Convert a valid HTML document to XML
  2. USAGE: python try_dom1.py < infile.html > outfile.xml
  3. """
  4. import
  5. sys
  6. from
  7. xml.dom
  8. import
  9. core
  10. from
  11. xml.dom.html_builder
  12. import
  13. HtmlBuilder
  14. # Construct an HtmlBuilder object and feed the data to it
  15. b = HtmlBuilder()
  16. b.feed(sys.stdin.read())
  17. # Get the newly-constructed document object
  18. doc = b.document
  19. # Output it as XML
  20. print
  21. 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

  1. """Build a DOM instance from scratch, write it to XML
  2. USAGE: python try_dom2.py > outfile.xml
  3. """
  4. import
  5. types
  6. from
  7. xml.dom
  8. import
  9. core
  10. from
  11. xml.dom.builder
  12. import
  13. Builder
  14. # Recursive function to build DOM instance from Python instance
  15. defobject_convert
  16. (builder, inst):
  17. # Put entire object inside an elem w/ same name as the class.
  18. builder.startElement(inst.__class__.__name__)
  19. for
  20. attr
  21. in
  22. inst.__dict__.keys():
  23. if
  24. attr[0] ==
  25. '_':
  26. # Skip internal attributes
  27. continue
  28. value = getattr(inst, attr)
  29. if
  30. type(value) == types.InstanceType:
  31. # Recursively process subobjects
  32. object_convert(builder, value)
  33. else
  34. :
  35. # Convert anything else to string, put it in an element
  36. builder.startElement(attr)
  37. builder.text(str(value))
  38. builder.endElement(attr)
  39. builder.endElement(inst.__class__.__name__)
  40. if
  41. __name__ ==
  42. '__main__':
  43. # Create container classes
  44. classquotations
  45. :
  46. pass
  47. classquotation
  48. :
  49. pass
  50. # Create an instance, fill it with hierarchy of attributes
  51. inst = quotations()
  52. inst.title =
  53. "Quotations file (not quotations.dtd conformant)"
  54. inst.quot1 = quot1 = quotation()
  55. quot1.text =
  56. """'"is not a quine" is not a quine' is a quine"""
  57. quot1.source =
  58. "Joshua Shagam, kuro5hin.org"
  59. inst.quot2 = quot2 = quotation()
  60. quot2.text =
  61. "Python is not a democracy. Voting doesn't help. "+\
  62. "Crying may..."
  63. quot2.source =
  64. "Guido van Rossum, comp.lang.python"
  65. # Create the DOM Builder
  66. builder = Builder()
  67. object_convert(builder, inst)
  68. print
  69. 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

  1. """Read in a DOM instance, convert it to a Python object
  2. """
  3. from
  4. xml.dom.utils
  5. import
  6. FileReader
  7. classPyObject
  8. :
  9. pass
  10. defpyobj_printer
  11. (py_obj, level=0):
  12. """Return a "deep" string description of a Python object"""
  13. from
  14. string
  15. import
  16. join, split
  17. import
  18. types
  19. descript =
  20. ''
  21. for
  22. membname
  23. in
  24. dir(py_obj):
  25. member = getattr(py_obj,membname)
  26. if
  27. type(member) == types.InstanceType:
  28. descript = descript + (
  29. ' '*level) +
  30. '{'+membname+
  31. '}\n'
  32. descript = descript + pyobj_printer(member, level+3)
  33. elif
  34. type(member) == types.ListType:
  35. descript = descript + (
  36. ' '*level) +
  37. '['+membname+
  38. ']\n'
  39. for
  40. i
  41. in
  42. range(len(member)):
  43. descript = descript+(
  44. ' '*level)+str(i+1)+
  45. ': '+ \
  46. pyobj_printer(member[i],level+3)
  47. else
  48. :
  49. descript = descript + membname+
  50. '='
  51. descript = descript + join(split(str(member)[:50]))+
  52. '...\n'
  53. return
  54. descript
  55. defpyobj_from_dom
  56. (dom_node):
  57. """Converts a DOM tree to a "native" Python object"""
  58. py_obj = PyObject()
  59. py_obj.PCDATA =
  60. ''
  61. for
  62. node
  63. in
  64. dom_node.get_childNodes():
  65. if
  66. node.name ==
  67. '#text':
  68. py_obj.PCDATA = py_obj.PCDATA + node.value
  69. elif
  70. hasattr(py_obj, node.name):
  71. getattr(py_obj, node.name).append(pyobj_from_dom(node))
  72. else
  73. :
  74. setattr(py_obj, node.name, [pyobj_from_dom(node)])
  75. return
  76. py_obj
  77. # Main test
  78. dom_obj = FileReader(
  79. "quotes.xml").document
  80. py_obj = pyobj_from_dom(dom_obj)
  81. if
  82. __name__ ==
  83. "__main__":
  84. print
  85. pyobj_printer(py_obj)

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

除了使用旧的 pyobj_printer() 类属函数(或者,更复杂和健壮的函数)之外,我们可以使用正常的属性记号来访问 py_obj 的元素。
Python 交互式会话

  1. >>>
  2. from
  3. try_dom3
  4. import
  5. *
  6. >>> py_obj.quotations[0].quotation[3].source[0].PCDATA
  7. 'Guido van Rossum, '

重新安排 DOM 树

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

  1. """Manipulate the arrangement of nodes in a DOM object
  2. """
  3. from
  4. try_dom3
  5. import
  6. *
  7. #-- Var 'doc' will hold the single <quotations> "trunk"
  8. doc = dom_obj.get_childNodes()[0]
  9. #-- Pull off all the nodes into a Python list
  10. # (each node is a <quotation> block, or a whitespace text node)
  11. nodes = []
  12. while
  13. 1:
  14. try
  15. : node = doc.removeChild(doc.get_childNodes()[0])
  16. except
  17. :
  18. break
  19. nodes.append(node)
  20. #-- Reverse the order of the quotations using a list method
  21. # (we could also perform more complicated operations on the list:
  22. # delete elements, add new ones, sort on complex criteria, etc.)
  23. nodes.reverse()
  24. #-- Fill 'doc' back up with our rearranged nodes
  25. for
  26. node
  27. in
  28. nodes:
  29. # if second arg is None, insert is to end of list
  30. doc.insertBefore(node, None)
  31. #-- Output the manipulated DOM
  32. print
  33. dom_obj.toxml()
  34. </quotation></quotations>

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

人气教程排行