2016-07-19 13 views
1

Ich habe eine XML-Datei zu parsen, und ich muss Elemente durch ID finden.Get etree Element mit Attribut oder Unterelement mit Attribut

Im Beispielcode, muss ich den Namen des driver finden, aber ich weiß nicht, ob meine ID für die vehicle, engine oder block ist. Ich möchte eine Lösung, die mit beliebigen XML innerhalb von vehicle arbeiten würde (aber Existenz von driver ist garantiert).

<road> 
    <vehicle id="16"> 
     <driver>Bob Johnson</driver> 
     <engine id="532"> 
      <type>V8</type> 
      <block id="113"> 
       <material>Aluminium</material> 
      </block> 
     </engine> 
    </vehicle> 
    <vehicle id="452"> 
     <driver>Dave Edwards</driver> 
     <engine id="212"> 
      <type>Inline 6</type> 
      <block id="381"> 
       <material>Cast Iron</material> 
      </block> 
     </engine> 
    </vehicle> 
</road> 

Was haben versucht, ich

Ich habe versucht, die Elemente durch ihre ID zu bekommen, und dann, wenn sie nicht vehicle Tags waren, den Baum Ebene hoch, es zu finden, aber es scheint, pythons elem.find() gibt None zurück, wenn das Ergebnis außerhalb elem ist.

bei the docs Sehen, sie haben dieses Beispiel:

# Nodes with name='Singapore' that have a 'year' child 
root.findall(".//year/..[@name='Singapore']") 

Aber ich sehe nicht, wie für jeden Nachkommen, diese Arbeit zu machen, wie es zu einem decendant auf einer bestimmten Ebene entgegengesetzt.

+0

sein kann es sich um eine persönliche Präferenz ist, aber da Sie schließlich 'lxml.etree' verwenden, ich denke,' Vorfahren-oder-selbst "sollte bevorzugt werden. Ziemlich sicher wäre es auch aus der Performance-Perspektive schneller. – alecxe

Antwort

1

Hinweis: Alle folgenden Snippets verwenden lxml Bibliothek. Führen Sie zum Installieren: pip install lxml.

Sie sollten root.xpath(..) nicht root.findall(..) verwenden.

>>> root.xpath("//vehicle/driver/text()") 
['Bob Johnson', 'Dave Edwards'] 

Wenn Sie Name des Fahrers von einem bestimmten ID extrahieren möchten, würden Sie tun:

>>> vehicle_id = "16" 
>>> xpath("//vehicle[@id='16' or .//*[@id='16']]/driver/text()") 
['Bob Johnson'] 

UPDATE: Um den Namen des Fahrers für eine bestimmte id auf jeder Ebene verschachtelt zu erhalten tiefer, Sie‘ d Sie:

>>> i = '16' 
>>> a.xpath("//vehicle[@id='%s' or .//*[@id='%s']]/driver/text()"%(i,i)) 
['Bob Johnson'] 
>>> i = '532' 
>>> a.xpath("//vehicle[@id='%s' or .//*[@id='%s']]/driver/text()"%(i,i)) 
['Bob Johnson'] 
>>> i = '113' 
>>> a.xpath("//vehicle[@id='%s' or .//*[@id='%s']]/driver/text()"%(i,i)) 
['Bob Johnson'] 
+0

OK, aber wie finde ich welche für meine ID? Wenn ich ID '381' habe, woher weiß ich, dass das für "Dave Edwards" und nicht für "Bob Johnson" ist? – nHaskins

+0

Kannst du es erklären? Möchten Sie einen Fahrer für ein Fahrzeug mit einer bestimmten ID finden? – SuperSaiyan

+0

Ja, oder für das Fahrzeug, das das Element mit der angegebenen ID enthält. Meine ID könnte für das Fahrzeug, den Motor oder den Block sein, aber ich muss den Fahrer finden. – nHaskins

1

wenn Sie die id kennen, aber nicht wissen, ob diese id aus Fahrzeug, Motor oder Block ist, können Sie es mit einem XPath expressio nähern n, aber Sie müssten lxml.etree anstelle von xml.etree.ElementTree verwenden (es hat sehr begrenzte XPath-Unterstützung).Verwenden Sie die ancestor-or-self Achse:

input_id = "your ID" 
print(root.xpath(".//*[@id='%s']/ancestor-or-self::vehicle/driver" % input_id)[0].text) 

Dies würde drucken:

  • Bob Johnson wenn input_id16 wäre oder 532 oder 113
  • Dave Edwards wenn input_id452 wäre oder 212 oder 381

komplette Arbeitsbeispiel:

import lxml.etree as ET 

data = """ 
<road> 
    <vehicle id="16"> 
     <driver>Bob Johnson</driver> 
     <engine id="532"> 
      <type>V8</type> 
      <block id="113"> 
       <material>Aluminium</material> 
      </block> 
     </engine> 
    </vehicle> 
    <vehicle id="452"> 
     <driver>Dave Edwards</driver> 
     <engine id="212"> 
      <type>Inline 6</type> 
      <block id="381"> 
       <material>Cast Iron</material> 
      </block> 
     </engine> 
    </vehicle> 
</road> 
""" 

root = ET.fromstring(data) 
for input_id in [16, 532, 113, 452, 212, 381]: 
    print(root.xpath(".//*[@id='%s']/ancestor-or-self::vehicle/driver" % input_id)[0].text) 

Drucke:

Bob Johnson 
Bob Johnson 
Bob Johnson 
Dave Edwards 
Dave Edwards 
Dave Edwards