[英]Find element that has unknown namespace in lxml
我有一個具有多個級別的 XML。 每個級別都可以附加命名空間。 我想find
一個我知道其名稱但不知道其名稱空間的特定元素。 例如:
my_file.xml
<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="aaa:bbb:ccc:ddd:eee">
<country name="Liechtenstein" xmlns="aaa:bbb:ccc:liechtenstein:eee">
<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" xmlns="aaa:bbb:ccc:singapore:eee">
<continent>Asia</continent>
<holidays>
<christmas>Yes</christmas>
</holidays>
<rank updated="yes">5</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama" xmlns="aaa:bbb:ccc:panama:eee">
<rank updated="yes">69</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
import lxml.etree as etree
tree = etree.parse('my_file.xml')
root = tree.getroot()
cntry_node = root.find('.//country')
上面的find
不會向cntry_node
返回任何內容。 在我的真實數據中,層次比這個例子更深。 lxml 文檔討論了命名空間。 當我這樣做時:
root.nsmap
我看到這個:
{None: 'aaa:bbb:ccc:ddd:eee'}
如果有人可以解釋如何訪問完整的nsmap
和/或如何使用它來find
特定元素? 非常感謝。
您可以聲明所有名稱空間,但鑒於您的示例 xml 的結構,我認為您最好完全忽略名稱空間而只使用local-name()
; 所以
cntry_node = root.xpath('.//*[local-name()="country"]')
cntry_node
返回
[<Element {aaa:bbb:ccc:liechtenstein:eee}country at 0x1cddf1d4680>,
<Element {aaa:bbb:ccc:singapore:eee}country at 0x1cddf1d47c0>,
<Element {aaa:bbb:ccc:panama:eee}country at 0x1cddf1d45c0>]
nsmap
不是 XML 文檔的所有命名空間的全局集合
我相信您的印象是nsmap
是 XML 文檔中存在的所有命名空間的集合。 並且該集合在解析文檔后可用。 事實並非如此。
nsmap
允許您訪問一個元素的命名空間定義。 所以這:
root = tree.getroot()
root.nsmap
為您提供root
元素上下文中已知的命名空間定義。 請記住,“root”只是 Python 變量的名稱,實際上包含 XML 文檔的最外層元素(我知道這一點是因為您調用了getroot()
)。 文檔的最外層元素是:
<data xmlns="aaa:bbb:ccc:ddd:eee">
所以預計它的 nsmap 將包含
{None: 'aaa:bbb:ccc:ddd:eee'}
(nsmap 中包含None
因為這是一個默認命名空間,沒有命名空間前綴,即 go ,其中None
是。)
XML 文檔的結構很糟糕
通常,處理命名空間的最佳方式是自己定義它們(而不是從輸入文檔中獲取它們)。 假設我們想找到以下元素:
<country name="Liechtenstein" xmlns="aaa:bbb:ccc:liechtenstein:eee">
此country
/地區元素位於默認命名空間中,命名空間 URI 為“aaa:bbb:ccc:liechtenstein:eee”。 要使用 lxml 找到它,請定義一個映射:
my_own_namespace_mapping = {'prefix': 'aaa:bbb:ccc:liechtenstein:eee'}
然后在檢索節點時使用它:
root.xpath('.//prefix:country', namespaces=my_own_namespace_mapping)
[<Element {aaa:bbb:ccc:liechtenstein:eee}country at 0x7fea87f363f8>]
但是,對於您的輸入文檔,您似乎需要為每個country
/地區元素單獨執行此操作,因為它們每個都在自己的默認命名空間中:
root.xpath('.//prefix:country', namespaces={'prefix': 'aaa:bbb:ccc:singapore:eee'})
[<Element {aaa:bbb:ccc:singapore:eee}country at 0x7fea879cfd40>]
等等。 這是非常不切實際的,不是因為 lxml 或命名空間很復雜,而是因為有人將這種 XML 格式設計得很糟糕。
順便說一句,一旦你找到了其中一個元素,你可以再次使用nsmap
來測試我上面所說的是否屬實:
root.xpath('.//prefix:country', namespaces={'prefix': 'aaa:bbb:ccc:liechtenstein:eee'})[0].nsmap
{None: 'aaa:bbb:ccc:liechtenstein:eee'}
另一種選擇是使用{*}
作為命名空間通配符...
cntry_node = root.find('.//{*}country')
注意:這只適用於find()
、 findall()
、 iter()
等; 不是xpath()
。
有關更多詳細信息,請參見此處。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.