簡體   English   中英

在 lxml 中查找具有未知名稱空間的元素

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM