2009-02-28 12 views
14

Sto provando ad analizzare dell'HTML con XPath. Seguendo l'esempio XML semplificato di seguito, voglio abbinare la stringa "Testo 1", quindi prendere il contenuto del nodo rilevante content.Come abbinare un nodo di testo quindi seguire i nodi parent usando XPath

<doc> 
    <block> 
     <title>Text 1</title> 
     <content>Stuff I want</content> 
    </block> 

    <block> 
     <title>Text 2</title> 
     <content>Stuff I don't want</content> 
    </block> 
</doc> 

codice mio Python lancia un traballante:

>>> from lxml import etree 
>>> 
>>> tree = etree.XML("<doc><block><title>Text 1</title><content>Stuff 
I want</content></block><block><title>Text 2</title><content>Stuff I d 
on't want</content></block></doc>") 
>>> 
>>> # get all titles 
... tree.xpath('//title/text()') 
['Text 1', 'Text 2'] 
>>> 
>>> # match 'Text 1' 
... tree.xpath('//title/text()="Text 1"') 
True 
>>> 
>>> # Follow parent from selected nodes 
... tree.xpath('//title/text()/../..//text()') 
['Text 1', 'Stuff I want', 'Text 2', "Stuff I don't want"] 
>>> 
>>> # Follow parent from selected node 
... tree.xpath('//title/text()="Text 1"/../..//text()') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "lxml.etree.pyx", line 1330, in lxml.etree._Element.xpath (src/ 
lxml/lxml.etree.c:14542) 
    File "xpath.pxi", line 287, in lxml.etree.XPathElementEvaluator.__ca 
ll__ (src/lxml/lxml.etree.c:90093) 
    File "xpath.pxi", line 209, in lxml.etree._XPathEvaluatorBase._handl 
e_result (src/lxml/lxml.etree.c:89446) 
    File "xpath.pxi", line 194, in lxml.etree._XPathEvaluatorBase._raise 
_eval_error (src/lxml/lxml.etree.c:89281) 
lxml.etree.XPathEvalError: Invalid type 

Questo è possibile in XPath? Devo esprimere ciò che voglio fare in un modo diverso?

risposta

22

Lo vuoi?

//title[text()='Text 1']/../content/text() 
+0

Duh, semplice, in realtà! Un po 'ha senso che sto selezionando l'attributo text() ora. – Mat

+2

puoi anche utilizzare // block [title = 'Text 1']/content per ottenere il nodo di contenuto pertinente – Dror

+0

@Dror: Ora è utile sapere. – Mat

16

Uso:

string(/*/*/title[. = 'Text 1']/following-sibling::content) 

Questo rappresenta almeno due miglioramenti in confronto alla soluzione attualmente accettata di Johannes Weiss:

  1. L'abbreviazione molto costoso " // " (di solito causa chi il documento XML da scansionare) viene evitato come dovrebbe essere ogni volta che la struttura del documento XML è conosciuta in anticipo.

  2. Non c'è ritorno indietro al genitore (il passo di locazione "/ .." è evitata)

+0

Un discreto miglioramento, il mio vero documento è HTML e la parte 'titolo' è annidata su cinque livelli profondi quindi devo tornare indietro di circa cinque genitori per arrivare all'area 'contenuto'. Porterò il primo punto in mente, anche se farà poca differenza per un hack sporco. – Mat

+2

Cosa fa '/ */* /' fare? Lo sto provando su un documento abbastanza grande e sembra lento come //. – dentarg

+2

@dentarg: '/ */*' seleziona tutti gli elementi che sono figli dell'elemento superiore del documento. È molto più veloce di '// someName' che attraversa il documento completo e seleziona ogni elemento chiamato' "someName" '. In questa risposta possiamo usare un'espressione ancora più efficiente: 'string (/ */*/title [. = 'Text 1'] [1]/following-sibling :: content)' L'espressione nella risposta non dovrebbe essere meno efficiente, dato un processore XPath ottimamente ottimizzato - perché ogni volta che la funzione 'stringa()' fornisce un argomento che è un insieme di nodi, produce solo il valore di stringa del primo nodo di questo set di nodi. –

Problemi correlati