近期楼主因为部分原因,需要对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是一种固有的分层数据格式,最自然的表示方式是用树。为此,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>
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内的各个节点的对应情况,否则不好使用。
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会处问题有时候。
利用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>
建议就是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>*
成功删除
当需要扩展子标签时候用etree.SubElement()这个方法。它有两个参数,第一个参数相当于是父亲,第二个参数相当于是儿子。
这样一直可以用这个方法扩展下去
如:
interface = etree.SubElement(data, 'inter')
..........................
out:
<data>
<inter>
...
后续再学习新知识。