python 解析XML(拼合互联网资料学习整理)

来源:互联网 发布:潮汕话软件 编辑:程序博客网 时间:2024/06/08 19:20

一、首先了解XML

如果你已经了解xml,可以跳过这一部分。

xml是一种描述层次结构化数据的通用方法。xml文档包含由起始和结束标签(tag)分隔的一个或多个元素(element)。以下也是一个完整的(虽然空洞)xml文件:

<foo>   </foo>  
这是foo元素的起始标签这是foo元素对应的结束标签。就如写作、数学或者代码中需要平衡括号一样,每一个起始标签必须有对应的结束标签来闭合(匹配)。

元素可以嵌套到任意层次。位于foo中的元素bar可以被称作其子元素

<foo>  <bar></bar></foo>

xml文档中的第一个元素叫做根元素(root element)。并且每份xml文档只能有一个根元素。以下不是一个xml文档,因为它存在两个“根元素”。

<foo></foo><bar></bar>

元素可以有其属性(attribute),它们是一些名字-值(name-value)对。属性由空格分隔列举在元素的起始标签中。一个元素中属性名不能重复。属性值必须用引号包围起来。单引号、双引号都是可以。

<foo lang='en'>                            <bar id='papayawhip' lang="fr"></bar>  </foo>
foo元素有一个叫做lang的属性。lang的值为enbar元素则有两个属性,分别为idlang。其中lang属性的值为fr。它不会与foo的那个属性产生冲突。每个元素都其独立的属性集。

如果元素有多个属性,书写的顺序并不重要。元素的属性是一个无序的键-值对集,跟Python中的列表对象一样。另外,元素中属性的个数是没有限制的。

元素可以有其文本内容(text content)

<foo lang='en'>  <bar lang='fr'>PapayaWhip</bar></foo>

如果某一元素既没有文本内容,也没有子元素,它也叫做空元素

<foo></foo>

表达空元素有一种简洁的方法。通过在起始标签的尾部添加/字符,我们可以省略结束标签。上一个例子中的xml文档可以写成这样:

<foo/>

就像Python函数可以在不同的模块(modules)中声明一样,也可以在不同的名字空间(namespace)中声明xml元素。xml文档的名字空间通常看起来像URL。我们可以通过声明xmlns来定义默认名字空间。名字空间声明跟元素属性看起来很相似,但是它们的作用是不一样的。

<feed xmlns='http://www.w3.org/2005/Atom'>    <title>dive into mark</title>             </feed>
feed元素处在名字空间http://www.w3.org/2005/Atom中。title元素也是。名字空间声明不仅会作用于当前声明它的元素,还会影响到该元素的所有子元素。

也可以通过xmlns:prefix声明来定义一个名字空间并取其名为prefix。然后该名字空间中的每个元素都必须显式地使用这个前缀(prefix)来声明。

<atom:feed xmlns:atom='http://www.w3.org/2005/Atom'>    <atom:title>dive into mark</atom:title>             </atom:feed>
feed元素属于名字空间http://www.w3.org/2005/Atomtitle元素也在那个名字空间。

对于xml解析器而言,以上两个xml文档是一样的。名字空间 + 元素名 = xml标识。前缀只是用来引用名字空间的,所以对于解析器来说,这些前缀名(atom:)其实无关紧要的。名字空间相同,元素名相同,属性(或者没有属性)相同,每个元素的文本内容相同,则xml文档相同。

最后,在根元素之前,字符编码信息可以出现在xml文档的第一行。(这里存在一个两难的局面(catch-22),直观上来说,解析xml文档需要这些编码信息,而这些信息又存在于xml文档中,如果你对xml如何解决此问题有兴趣,请参阅xml规范中 F 章节)

<?xml version='1.0' encoding='utf-8'?>

XML 的声明

<?xml version=”1.0” standalone=”yes” encoding=”UTF-8”?>

    这是一个XML处理指令。处理指令以 <? 开始,以 ?> 结束。<? 后的第一个单词是指令名,如xml, 代表XML声明。    version, standalone, encoding 是三个特性,特性是由等号分开的名称-数值对,等号左边是特性名称,等号右边是特性的值,用引号引起来。几点解释:

  • version: 说明这个文档符合1.0规范
  • standalone: 说明文档在这一个文件里还是需要从外部导入, standalone 的值设为yes 说明所有的文档都在这一文件里完成 
  • encoding: 指文档字符编码

XML 根元素定义

XML文档的树形结构要求必须有一个根元素。根元素的起始标记要放在所有其它元素起始标记之前,根元素的结束标记根放在其它所有元素的结束标记之后,如

 

<?xml version=”1.0” standalone=”yes” encoding=”UTF-8”?><Settings><Person>Zhang San</Person></Settings>

 

XML元素

元素的基本结构由 开始标记,数据内容,结束标记组成,如

<Person>  <Name>Zhang San</Name>  <Sex>Male</Sex></Person>

需要注意的是:

  • 元素标记区分大小写,<Name> <name>是两个不同的标记
  • 结束标记必须有反斜杠,如 </Name>

XML元素标记命名规则如下:

  • 名字中可以包含字母,数字及其它字母
  • 名字不能以数字或下划线开头
  • 名字不能用xml开头
  • 名字中不能包含空格和冒号

XML中的注释

XML中注释如下:

<!-- this is comment -->

需要注意的是:

  • 注释中不要出现“--”或“-
  • 注释不要放在标记中
  • 注释不能嵌套 

5  PI  (Processing Instruction)PI Processing Instruction, 处理指令。PI以“<?”开头,以“?>”结束,用来给下游的文档传递信息。

<?xml:stylesheet href=”core.css” type=”text/css” ?>

例子表明这个XML文档用core.css控制显示。

参考 http://www.cnblogs.com/jb8164/articles/736515.html 简单讲解XML

现在我们已经知道足够多的xml知识,可以开始探险了! 

二、python操作XML

例如

<?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom'xml:lang='en'>
 
<title>dive into mark</title>
 
<subtitle>currently between addictions</subtitle>
 
<id>tag:diveintomark.org,2001-07-29:/</id>
 
<updated>2009-03-27T21:56:07Z</updated>
 
<link rel='alternate'type='text/html'href='http://diveintomark.org/'/>
 
<link rel='self'type='application/atom+xml'href='http://diveintomark.org/feed/'/>
 
<entry>
   
<author>
     
<name>Mark</name>
     
<uri>http://diveintomark.org/</uri>
   
</author>
   
<title>Dive into history, 2009 edition</title>
   
<link rel='alternate'type='text/html'
     
href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/>
   
<id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id>
   
<updated>2009-03-27T21:56:07Z</updated>
   
<published>2009-03-27T17:20:42Z</published>
   
<categoryscheme='http://diveintomark.org'term='diveintopython'/>
   
<categoryscheme='http://diveintomark.org'term='docbook'/>
   
<categoryscheme='http://diveintomark.org'term='html'/>
 
<summary type='html'>Putting an entire chapter on one page sounds
    bloated, but consider this &amp;mdash; my longest chapter so far
    would be 75 printed pages, and it loads in under 5 seconds&amp;hellip;
    On dialup.
</summary>
 
</entry>
 
<entry>
   
<author>
     
<name>Mark</name>
     
<uri>http://diveintomark.org/</uri>
   
</author>
   
<title>Accessibility is a harsh mistress</title>
   
<link rel='alternate'type='text/html'
     
href='http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress'/>
   
<id>tag:diveintomark.org,2009-03-21:/archives/20090321200928</id>
   
<updated>2009-03-22T01:05:37Z</updated>
   
<published>2009-03-21T20:09:28Z</published>
   
<categoryscheme='http://diveintomark.org'term='accessibility'/>
   
<summary type='html'>The accessibility orthodoxy does not permit people to
      question the value of features that are rarely useful and rarely used.
</summary>
 
</entry>
 
<entry>
   
<author>
     
<name>Mark</name>
   
</author>
   
<title>A gentle introduction to video encoding, part 1: container formats</title>
   
<link rel='alternate'type='text/html'
     
href='http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats'/>
   
<id>tag:diveintomark.org,2008-12-18:/archives/20081218155422</id>
   
<updated>2009-01-11T19:39:22Z</updated>
   
<published>2008-12-18T15:54:22Z</published>
   
<categoryscheme='http://diveintomark.org'term='asf'/>
   
<categoryscheme='http://diveintomark.org'term='avi'/>
   
<categoryscheme='http://diveintomark.org'term='encoding'/>
   
<categoryscheme='http://diveintomark.org'term='flv'/>
   
<categoryscheme='http://diveintomark.org'term='GIVE'/>
   
<categoryscheme='http://diveintomark.org'term='mp4'/>
   
<categoryscheme='http://diveintomark.org'term='ogg'/>
   
<categoryscheme='http://diveintomark.org'term='video'/>
   
<summary type='html'>These notes will eventually become part of a
      tech talk on video encoding.
</summary>
 
</entry>
</feed><?xml version='1.0' encoding='utf-8'?>
<feed xmlns='http://www.w3.org/2005/Atom'xml:lang='en'>
 
<title>dive into mark</title>
 
<subtitle>currently between addictions</subtitle>
 
<id>tag:diveintomark.org,2001-07-29:/</id>
 
<updated>2009-03-27T21:56:07Z</updated>
 
<link rel='alternate'type='text/html'href='http://diveintomark.org/'/>
 
<link rel='self'type='application/atom+xml'href='http://diveintomark.org/feed/'/>
 
<entry>
   
<author>
     
<name>Mark</name>
     
<uri>http://diveintomark.org/</uri>
   
</author>
   
<title>Dive into history, 2009 edition</title>
   
<link rel='alternate'type='text/html'
     
href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/>
   
<id>tag:diveintomark.org,2009-03-27:/archives/20090327172042</id>
   
<updated>2009-03-27T21:56:07Z</updated>
   
<published>2009-03-27T17:20:42Z</published>
   
<categoryscheme='http://diveintomark.org'term='diveintopython'/>
   
<categoryscheme='http://diveintomark.org'term='docbook'/>
   
<categoryscheme='http://diveintomark.org'term='html'/>
 
<summary type='html'>Putting an entire chapter on one page sounds
    bloated, but consider this &amp;mdash; my longest chapter so far
    would be 75 printed pages, and it loads in under 5 seconds&amp;hellip;
    On dialup.
</summary>
 
</entry>
 
<entry>
   
<author>
     
<name>Mark</name>
     
<uri>http://diveintomark.org/</uri>
   
</author>
   
<title>Accessibility is a harsh mistress</title>
   
<link rel='alternate'type='text/html'
     
href='http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress'/>
   
<id>tag:diveintomark.org,2009-03-21:/archives/20090321200928</id>
   
<updated>2009-03-22T01:05:37Z</updated>
   
<published>2009-03-21T20:09:28Z</published>
   
<categoryscheme='http://diveintomark.org'term='accessibility'/>
   
<summary type='html'>The accessibility orthodoxy does not permit people to
      question the value of features that are rarely useful and rarely used.
</summary>
 
</entry>
 
<entry>
   
<author>
     
<name>Mark</name>
   
</author>
   
<title>A gentle introduction to video encoding, part 1: container formats</title>
   
<link rel='alternate'type='text/html'
     
href='http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats'/>
   
<id>tag:diveintomark.org,2008-12-18:/archives/20081218155422</id>
   
<updated>2009-01-11T19:39:22Z</updated>
   
<published>2008-12-18T15:54:22Z</published>
   
<categoryscheme='http://diveintomark.org'term='asf'/>
   
<categoryscheme='http://diveintomark.org'term='avi'/>
   
<categoryscheme='http://diveintomark.org'term='encoding'/>
   
<categoryscheme='http://diveintomark.org'term='flv'/>
   
<categoryscheme='http://diveintomark.org'term='GIVE'/>
   
<categoryscheme='http://diveintomark.org'term='mp4'/>
   
<categoryscheme='http://diveintomark.org'term='ogg'/>
   
<categoryscheme='http://diveintomark.org'term='video'/>
   
<summary type='html'>These notes will eventually become part of a
      tech talk on video encoding.
</summary>
 
</entry>
</feed>

Python可以使用几种不同的方式解析xml文档。它包含了dom和sax解析器,但是我们焦点将放在另外一个叫做ElementTree的库上边。

跳过该代码清单

[隐藏] [在新窗口中打开] [download feed.xml]
>>> import xml.etree.ElementTree as etree >>> tree = etree.parse('examples/feed.xml') >>> root = tree.getroot() >>> root <Element {http://www.w3.org/2005/Atom}feed at cd1eb0>

 

ElementTree属于Python标准库的一部分,它的位置为xml.etree.ElementTreeparse()函数是ElementTree库的主要入口,它使用文件名或者流对象作为参数。parse()函数会立即解析完整个文档。如果内存资源紧张,也可以增量式地解析xml文档parse()函数会返回一个能代表整篇文档的对象。这不是根元素。要获得根元素的引用可以调用getroot()方法。如预期的那样,根元素即http://www.w3.org/2005/Atom名字空间中的feed。该字符串表示再次重申了非常重要的一点:xml元素由名字空间和标签名(也称作本地名(local name))组成。这篇文档中的每个元素都在名字空间Atom中,所以根元素被表示为{http://www.w3.org/2005/Atom}feed

 

ElementTree使用{namespace}localname来表达xml元素。我们将会在ElementTree的api中多次见到这种形式。

元素即列表#

在ElementTree API中,元素的行为就像列表一样。列表中的项即该元素的子元素。

跳过该代码清单

[隐藏] [在新窗口中打开]
# continued from the previous example>>> root.tag '{http://www.w3.org/2005/Atom}feed'>>> len(root) 8>>> for child in root: ...   print(child) ... <Element {http://www.w3.org/2005/Atom}title at e2b5d0><Element {http://www.w3.org/2005/Atom}subtitle at e2b4e0><Element {http://www.w3.org/2005/Atom}id at e2b6c0><Element {http://www.w3.org/2005/Atom}updated at e2b6f0><Element {http://www.w3.org/2005/Atom}link at e2b4b0><Element {http://www.w3.org/2005/Atom}entry at e2b720><Element {http://www.w3.org/2005/Atom}entry at e2b510><Element {http://www.w3.org/2005/Atom}entry at e2b750>

 

紧接前一例子,根元素为{http://www.w3.org/2005/Atom}feed根元素的“长度”即子元素的个数。我们可以像使用迭代器一样来遍历其子元素。从输出可以看到,根元素总共有8个子元素:所有feed级的元数据(titlesubtitleidupdatedlink),还有紧接着的三个entry元素。

 

也许你已经注意到了,但我还是想要指出来:该列表只包含直接子元素。每一个entry元素都有其子元素,但是并没有包括在这个列表中。这些子元素本可以包括在entry元素的列表中,但是确实不属于feed的子元素。但是,无论这些元素嵌套的层次有多深,总是有办法定位到它们的;在这章的后续部分我们会介绍两种方法。

属性即字典#

xml不只是元素的集合;每一个元素还有其属性集。一旦获取了某个元素的引用,我们可以像操作Python的字典一样轻松获取到其属性。

跳过该代码清单

[隐藏] [在新窗口中打开]
# continuing from the previous example>>> root.attrib {'{http://www.w3.org/XML/1998/namespace}lang': 'en'}>>> root[4] <Element {http://www.w3.org/2005/Atom}link at e181b0>>>> root[4].attrib {'href': 'http://diveintomark.org/', 'type': 'text/html', 'rel': 'alternate'}>>> root[3] <Element {http://www.w3.org/2005/Atom}updated at e2b4e0>>>> root[3].attrib {}

 

attrib是一个代表元素属性的字典。这个地方原来的标记语言是这样描述的:<feed xmlns='http://www.w3.org/2005/Atom' xml:lang='en'>。前缀xml:指示一个内置的名字空间,每一个xml不需要声明就可以使用它。第五个子元素 — 以0为起始的列表中即[4] — 为元素linklink元素有三个属性:hreftype,和rel第四个子元素 — [3] — 为updated

元素updated没有子元素,所以.attrib是一个空的字典对象。

 

元素为列表,可以用片段操作符操作,

属性为字典,有键和值。

 

在XML文档中查找结点#

到目前为止,我们已经“自顶向下“地从根元素开始,一直到其子元素,走完了整个文档。但是许多情况下我们需要找到xml中特定的元素。Etree也能完成这项工作。

跳过该代码清单

[隐藏] [在新窗口中打开]
>>> import xml.etree.ElementTree as etree>>> tree = etree.parse('examples/feed.xml')>>> root = tree.getroot()>>> root.findall('{http://www.w3.org/2005/Atom}entry') [<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>, <Element {http://www.w3.org/2005/Atom}entry at e2b510>, <Element {http://www.w3.org/2005/Atom}entry at e2b540>]>>> root.tag'{http://www.w3.org/2005/Atom}feed'>>> root.findall('{http://www.w3.org/2005/Atom}feed') []>>> root.findall('{http://www.w3.org/2005/Atom}author') []

 

findfall()方法查找匹配特定格式的子元素。(关于查询的格式稍后会讲到。)每个元素 — 包括根元素及其子元素 — 都有findall()方法。它会找到所有匹配的子元素。但是为什么没有看到任何结果呢?也许不太明显,这个查询只会搜索其子元素。由于根元素feed中不存在任何叫做feed的子元素,所以查询的结果为一个空的列表。这个结果也许也在你的意料之外。在这篇文档中确实存在author元素;事实上总共有三个(每个entry元素中都有一个)。但是那些author元素不是根元素的直接子元素。我们可以在任意嵌套层次中查找author元素,但是查询的格式会有些不同。

 

跳过该代码清单

[隐藏] [在新窗口中打开]
>>> tree.findall('{http://www.w3.org/2005/Atom}entry') [<Element {http://www.w3.org/2005/Atom}entry at e2b4e0>, <Element {http://www.w3.org/2005/Atom}entry at e2b510>, <Element {http://www.w3.org/2005/Atom}entry at e2b540>]>>> tree.findall('{http://www.w3.org/2005/Atom}author') []

 

为了方便,对象tree(调用etree.parse()的返回值)中的一些方法是根元素中这些方法的镜像。在这里,如果调用tree.getroot().findall(),则返回值是一样的。也许有些意外,这个查询请求也没有找到文档中的author元素。为什么没有呢?因为它只是tree.getroot().findall('{http://www.w3.org/2005/Atom}author')的一种简洁表示,即“查询所有是根元素的子元素的author”。因为这些authorentry元素的子元素,所以查询没有找到任何匹配的。

 

find()方法用来返回第一个匹配到的元素。当我们认为只会有一个匹配,或者有多个匹配但我们只关心第一个的时候,这个方法是很有用的。

跳过该代码清单

[隐藏] [在新窗口中打开]
>>> entries = tree.findall('{http://www.w3.org/2005/Atom}entry') >>> len(entries)3>>> title_element = entries[0].find('{http://www.w3.org/2005/Atom}title') >>> title_element.text'Dive into history, 2009 edition'>>> foo_element = entries[0].find('{http://www.w3.org/2005/Atom}foo') >>> foo_element>>> type(foo_element)<class 'NoneType'>

 

在前一样例中已经看到。这一句返回所有的atom:entry元素。find()方法使用ElementTree作为参数,返回第一个匹配到的元素。entries[0]中没有叫做foo的元素,所以返回值为None

 

可逮住你了,在这里find()方法非常容易被误解。在布尔上下文中,如果ElementTree元素对象不包含子元素,其值则会被认为是False如果len(element)等于0)。这就意味着if element.find('...')并非在测试是否find()方法找到了匹配项;这条语句是在测试匹配到的元素是否包含子元素!想要测试find()方法是否返回了一个元素,则需使用if element.find('...') is not None

可以在所有派生(descendant)元素中搜索,任意嵌套层次的子元素,孙子元素等…

跳过该代码清单

[隐藏] [在新窗口中打开]
>>> all_links = tree.findall('//{http://www.w3.org/2005/Atom}link') >>> all_links[<Element {http://www.w3.org/2005/Atom}link at e181b0>, <Element {http://www.w3.org/2005/Atom}link at e2b570>, <Element {http://www.w3.org/2005/Atom}link at e2b480>, <Element {http://www.w3.org/2005/Atom}link at e2b5a0>]>>> all_links[0].attrib {'href': 'http://diveintomark.org/', 'type': 'text/html', 'rel': 'alternate'}>>> all_links[1].attrib {'href': 'http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition', 'type': 'text/html', 'rel': 'alternate'}>>> all_links[2].attrib{'href': 'http://diveintomark.org/archives/2009/03/21/accessibility-is-a-harsh-mistress', 'type': 'text/html', 'rel': 'alternate'}>>> all_links[3].attrib{'href': 'http://diveintomark.org/archives/2008/12/18/give-part-1-container-formats', 'type': 'text/html', 'rel': 'alternate'}

 

//{http://www.w3.org/2005/Atom}link与前一样例很相似,除了开头的两条斜线。这两条斜线告诉findall()方法“不要只在直接子元素中查找;查找的范围可以是任意嵌套层次”。查询到的第一个结果根元素的直接子元素。从它的属性中可以看出,它是一个指向该feed的html版本的备用链接。其他的三个结果分别是低一级的备用链接。每一个entry都有单独一个link子元素,由于在查询语句前的两条斜线的作用,我们也能定位到他们。

 

总的来说,ElementTree的findall()方法是其一个非常强大的特性,但是它的查询语言却让人有些出乎意料。官方描述它为“有限的XPath支持。”XPath是一种用于查询xml文档的W3C标准。对于基础地查询来说,ElementTree与XPath语法上足够相似,但是如果已经会XPath的话,它们之间的差异可能会使你感到不快。现在,我们来看一看另外一个第三方xml库,它扩展了ElementTree的api以提供对XPath的全面支持。

重点:

用根元素,这样可以查找任意元素。

查找第一个节点,用find

查找一个元素的所有直接子元素findall()

加上两条斜线。这两条斜线告诉findall()方法“不要只在直接子元素中查找;查找的范围可以是任意嵌套层次”。

Element中的遍历与查询

Element.iter(tag=None):遍历该Element所有后代,也可以指定tag进行遍历寻找。

Element.findall(path):查找当前元素下tag或path能够匹配的直系节点。

Element.find(path):查找当前元素下tag或path能够匹配的首个直系节点。

Element.text: 获取当前元素的text值。

Element.get(key, default=None):获取元素指定key对应的属性值,如果没有该属性,则返回default值。

参考:

http://www.cnblogs.com/ifantastic/archive/2013/04/12/3017110.html   Python标准库之xml.etree.ElementTree
http://www.w3school.com.cn/xmldom/dom_methods.asp  XML DOM - 属性和方法
http://blog.csdn.net/yueguanghaidao/article/details/7265246 例子
http://blog.csdn.net/menglei8625/article/details/7494509  Python_使用ElementTree解析xml文件
http://www.cnblogs.com/jb8164/articles/736515.html 简单讲解XML
http://woodpecker.org.cn/diveintopython3/xml.html  系统讲XML解析和python解析 不错


 

 

0 0