python中的XML解析问题(增删改查)

xml.etree.ElementTree知识

近期楼主因为部分原因,需要对XML进行一定的操作,因此初步学习了一系列基础的增删改查的操作,如果后续又复杂的在学习。推荐以下两个网址用于自学:
https://docs.python.org/3/library/xml.etree.elementtree.html#module-xml.etree.ElementTree
https://www.runoob.com/python3/python3-xml-processing.html
我主要是学习xml.etree.ElementTree的部分知识。

XML基础

XML是一种固有的分层数据格式,最自然的表示方式是用树。为此,ET有两个类—ElementTree将整个XML文档表示为树,而Element表示树中的单个节点。与整个文档的交互(读写文件)通常在ElementTree级别完成。与单个XML元素及其子元素的交互是在元素级别完成的
以下所有例子均以sample.xml展开:

<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank>1</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank>4</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank>68</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>

a.根节点

import xml.etree.ElementTree as ET
tree = ET.parse('./sample.xml')
root = tree.getroot()
print(tree)
print(root)
..........................
out:
<xml.etree.ElementTree.ElementTree object at 0x00000292754E84E0>
<Element 'data' at 0x0000029275256598>

其中root为根节点,其中输出根节点的名字’data’,作为一个元素,root有一个标记和一个属性字典。

print(root.tag,root.attrib)
................................
out:
data {}
for child in root:
	print(child)
	print(child.tag)
	print(child.attrib)
	print('*'*50)
......................
out:
<Element 'country' at 0x0000022A2B586688>
country
{'name': 'Liechtenstein'}
**************************************************
<Element 'country' at 0x0000022A2B888458>
country
{'name': 'Singapore'}
**************************************************
<Element 'country' at 0x0000022A2B8885E8>
country
{'name': 'Panama'}
**************************************************	

由上述可以看出所谓的tag就是<>内的第一个XX其与某一段的最后一个是相同的如country:

<***country*** name="Panama">
        <rank>68</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </***country***>

而attrib就是一个键值对:

<country ***name="Panama"***>
        <rank>68</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>

每一个<>都是一个上述的规律的,对于节点country也是一样的如下述代码对于第一个country:

country = root.find('country')    
for child in country:             
	print(child)                  
	print(child.tag)              
	print(child.attrib)           
	print('*'*50)    
............................
out
<Element 'rank' at 0x000001DF34839F98>
rank
{}
**************************************************
<Element 'year' at 0x000001DF34882C28>
year
{}
**************************************************
<Element 'gdppc' at 0x000001DF34882C78>
gdppc
{}
**************************************************
<Element 'neighbor' at 0x000001DF348A0318>
neighbor
{'name': 'Austria', 'direction': 'E'}
**************************************************
<Element 'neighbor' at 0x000001DF348A8408>
neighbor
{'name': 'Switzerland', 'direction': 'W'}
**************************************************             

不同级别的根节点他的<>之间是有所进的,因此简单对此做一个记录,预防后期自己忘记。
两个<>…<>之间的内容为text,根据不同级别的节点,可以用索引的方式进行读取,如

print(root[0][1].text)
print(root[0][3].text)
.............................
out:
2008
None

利用序号进行索引前提是你知道这个xml内的各个节点的对应情况,否则不好使用。

b.寻找元素

1.首先利用迭代的方式 即root.iter(‘要寻找的节点的tag’),此类方式一般从根节点出发比较好,因为你的其他节点都是由根节点找到的。这种方法儿子,侄子,孙子都能找。

for neighbor in root.iter('neighbor'):             
    print(neighbor.attrib)    
 ...................................
 out:                     
{'name': 'Austria', 'direction': 'E'}
{'name': 'Switzerland', 'direction': 'W'}
{'name': 'Malaysia', 'direction': 'N'}
{'name': 'Costa Rica', 'direction': 'W'}
{'name': 'Colombia', 'direction': 'E'}

2.利用findall(‘要寻找的节点的tag’),他只会返回你当前XX.findall(‘要寻找的节点的tag’)中的XX的直接子节点的东西,也就是说她不会跨越他的侄子他只会找自己的儿子,而且是所有的儿子,况且孙子都不可以,可以自行尝试,比如你的第一个子结点中有某一个子节点的tag也是country,那么利用上述findall方式是找不到的。如:

<?xml version="1.0"?>
<data>
    <*country* name="Liechtenstein">
        <rank>1</rank>
        <*country*>2008</country>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    ...
```python
for country in root.findall('country'):
	print(country.tag)
.....................................
out:
country
输出的是一个country而不是两个country

3.find()
类似与findall,但是他只返回第一个符合条件的而不是所有的。只会找第一个儿子
建议就是root.iter()与find连用而不要与findall(‘’)连用
在删除操作遍历时建议用findall,而不是iter,我不知道为啥,只是实际操作中用iter会处问题有时候。

c.更新/增加XML内的内容

利用XX.set(‘a’,‘b’),可以为tag为XX的某个节点增加一个键值对即a = 'b,如果键原来就存在,那么其值会被新的值覆盖,这个与字典类似’
合理利用XX.text可以更改某个节点的text,其中text返回的是str
利用root.write(path)进行保存
利用xx.get(‘attrib中的键’)可以获取其相应的值
如:

for rank in root.iter('rank'):
	new_rank = int(rank.text) + 1
	rank.text = str(new_rank)
	rank.set('updated', 'yes')
tree.write('./output.xml')
......................................
out:
<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank updated="yes">69</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>

d.删除XML内容

建议就是root.iter()与find连用而不要与findall(‘’)连用
在删除操作遍历时建议用findall,而不是iter,我不知道为啥,只是实际操作中用iter会处问题有时候。
利用Eleme.remove()

for country in root.**findall**('country'):
	rank = int(country.**find**('rank').text)
	if rank > 50:
		root.remove(country)
tree.write('output.xml')
这里findall换成root.iter()结果不正确,暂时不知道原因,希望提出。

NOTE:
删除过程中只能对自己的直接一级操作,也就是只能删除儿子辈,孙子辈没法删除。如以下代码:

for country in root.findall('country'):   
    rank = country.find('rank')           
    rank1 = int(country.find('rank').text)
    if rank1 > 50:                        
        **root**.remove(**rank**)   # 爷孙关系              
tree.write('output.xml')   
..............................................
out:
ValueError: list.remove(x): x not in list               

再看这段代码:

for country in root.findall('country'):   
    rank = country.find('rank')           
    rank1 = int(country.find('rank').text)
    if rank1 > 50:                        
        **country**.remove(**rank**)   # 父子关系              
tree.write('output.xml')   
.........................................................
out:
<data>
    <country name="Liechtenstein">
        <rank>1</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor direction="E" name="Austria" />
        <neighbor direction="W" name="Switzerland" />
    </country>
    <country name="Singapore">
        <rank>4</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor direction="N" name="Malaysia" />
    </country>
    *<country name="Panama">
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor direction="W" name="Costa Rica" />
        <neighbor direction="E" name="Colombia" />
    </country>
</data>*
成功删除

e.新建一个XML

当需要扩展子标签时候用etree.SubElement()这个方法。它有两个参数,第一个参数相当于是父亲,第二个参数相当于是儿子。
这样一直可以用这个方法扩展下去

如:

interface = etree.SubElement(data, 'inter')
..........................
out:
<data>
	<inter>
	...

后续再学习新知识。

你可能感兴趣的:(python中的XML解析问题(增删改查))