Xpath: appunti
Indice:
parte 1. Xpath: Appunti
parte 2. xpath: Abbreviazioni
parte 3. xpath: Funzioni generiche
scrapy
xpath syntax
Da wikipedia:
In informatica XPath è un linguaggio, parte della famiglia XML, che permette di individuare i nodi all’interno di un documento XML.
Le espressioni XPath, a differenza delle espressioni XML, non servono solo ad identificare la struttura di un documento,
bensì a localizzarne con precisione i nodi.
Per poter fare pratica, utilizzeremo la shell di scrapy, quindi passiamo all’installazione.
Installazione Scrapy
Per prima cosa creare un virtual-environment.
virtualenv venv
Abilitare il virtual-environment:
venv/Script/activate
per windows, mentre per linux, nel mio specifico caso Ubuntu:
source venv/bin/activate
Prima di procedere con l’installazione dei pacchetti necessari tramite
“pip”, per ubuntu (>12.04) è necessario installare alcuni pacchetti necessari
alle successive compilazioni, quindi da terminale:
sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
sempre da venv, se stiamo lavorando da window, installare il pacchetto “pypiwin32”:
pip install pypiwin32
ora, per entrambi i casi, è possibile installare Scrapy:
pip install scrapy
Shell
Per lanciare la shell di scrapy utilizzare il comando:
scrapy shell
Scrapy ci mette a disposizione un oggetto Selector sul quale è possibile
utilizzare xpath.
>>> from scrapy import Selector >>> s = Selector(text="<foo><bar></bar></foo>") >>> s.xpath('//bar').extract() [u'<bar></bar>']
Nota:
Le espressioni xpath, oltre con Selector, possono essere analizzate direttamente
sull’oggetto response, aprendo la shell e passando come argomento il percorso di un
file o l’indirizzo di una pagina web:
(venv) C:\...>scrapy shell "file://path_to_file" >>> response.xpath('//*').extract()
Qualora volessimo modificare il body dell’oggetto response,
dobbiamo fare una copia di esso utilizzando il metodo replace():
>>> new_body = "<foo><bar></bar></foo>" >>> response.body = new_body ... raise AttributeError(msg) AttributeError: HtmlResponse.body is not modifiable, use HtmlResponse.replace() instead >>> new_response = response.replace(body=new_body) >>> new_response.xpath('//bar') [<Selector xpath='//bar' data=u'<bar></bar>'>]
Diamo ora un’occhiata molto generica a xpath.
Xpath: parentele
Supponiamo di avere una struttura di questo genere:
<A-TAG class="a-class">A-text <B-TAG class="b-class">B-text</B-TAG> <B-TAG id="1">B-text <C-TAG class="c-class" id="3">C-text</C-TAG> <C-TAG class="c-class" id="4">C-text <D-TAG>D-text</D-TAG> </C-TAG> </B-TAG> <B-TAG id="2">B-text <C-TAG id="5">C-text <D-TAG>D-text</D-TAG> <D-TAG id="6">D-text <E-TAG>E-text</E-TAG> <E-TAG>E-text <F-TAG>F-text</F-TAG> </E-TAG> </D-TAG> </C-TAG> </B-TAG> <B-TAG attr="b-attr">B-text <C-TAG>C-text <D-TAG>D-text</D-TAG> </C-TAG> </B-TAG> </A-TAG>
prepariamo il selector con il testo precedente:
>>> body=""" ... <A-TAG class="a-class">A-text ... <B-TAG class="b-class">B-text</B-TAG> ... <B-TAG id="1">B-text ... <C-TAG class="c-class" id="3">C-text</C-TAG> ... <C-TAG class="c-class" id="4">C-text ... <D-TAG>D-text</D-TAG> ... </C-TAG> ... </B-TAG> ... <B-TAG id="2">B-text ... <C-TAG id="5">C-text ... <D-TAG>D-text</D-TAG> ... <D-TAG id="6">D-text ... <E-TAG>E-text</E-TAG> ... <E-TAG>E-text ... <F-TAG>F-text</F-TAG> ... </E-TAG> ... </D-TAG> ... </C-TAG> ... </B-TAG> ... <B-TAG attr="b-attr">B-text ... <C-TAG>C-text ... <D-TAG>D-text</D-TAG> ... </C-TAG> ... </B-TAG> ... </A-TAG> ... """ >>> selector = Selector(text=body)
Nota:
Prima di analizzare le parentele è bene fare una distinzione sui percorsi.
Se l’espressione xpath comincia con ‘/’ si vogliono ricercare percorsi assoluti,
mentre con ‘//’ si cercano TUTTi gli elementi che sposano i criteri successivi.
Iniziamo ad analizzare le parentele ricordandoci di utilizzare il filtri tag in MINUSCOLO!
>>> selector <Selector xpath=None data=u'<html><body><a-tag class="a-class">A-tex...' >>>> len(selector.xpath('//B-TAG')) 0 >>> len(selector.xpath('//b-tag')) 4
ancestor::
Ritorna tutti i predecessori (ancestors ovvero genitori, genitori di genitori e così via)
E’ importantissimo ricordarsi di chiamare la funzione position() che decide quale
livello di ancestor si è scelto di conoscere:
>>> selector = Selector(text=body) >>> for pos in range(1, 4): selector.xpath('//c-tag[@id=4]/ancestor::*[position() = %s]' % pos) ... [<Selector xpath='//c-tag[@id=4]/ancestor::*[position() = 1]' data=u'<b-tag id="1">B-text\n <c-tag clas'>] [<Selector xpath='//c-tag[@id=4]/ancestor::*[position() = 2]' data=u'<a-tag class="a-class">A-text\n <b-tag'>] [<Selector xpath='//c-tag[@id=4]/ancestor::*[position() = 3]' data=u'<body><a-tag class="a-class">A-text\n '>]
All’aumentare del numero di posizione, ci allontaniamo con il predecessore: 1=b-tag, 2=a-tag, 3=”body”
‘c-tag[@id=4]’ serve a filtrare tutti i c-tag con attributo id=4, se omettessi questo filtro,
otterrei gli ancestors di tutti i nodi c-tag (o ch iper lui):
>>> for s in selector.xpath('//c-tag/ancestor::*[position() = 1]'): print s ... <Selector xpath='//c-tag/ancestor::*[position() = 1]' data=u'<b-tag id="1">B-text\n <c-tag clas'> <Selector xpath='//c-tag/ancestor::*[position() = 1]' data=u'<b-tag id="2">B-text\n <c-tag id="'> <Selector xpath='//c-tag/ancestor::*[position() = 1]' data=u'<b-tag attr="b-attr">B-text\n <c-t'>
ancestor-or-self::
Ritorna il nodo corrente (self) e come nel caso precedente tutti i predecessori (ancestors).
In questo caso la posizione 1 corrisponde a self (il nodo stesso)
>>> for pos in range(1, 5): ... selector.xpath('//c-tag[@id=4]/ancestor-or-self::*[position() = %s]' % pos) ... [<Selector xpath='//c-tag[@id=4]/ancestor-or-self::*[position() = 1]' data=u'<c-tag class="c-class" id="4">C-text\n '>] [<Selector xpath='//c-tag[@id=4]/ancestor-or-self::*[position() = 2]' data=u'<b-tag id="1">B-text\n <c-tag clas'>] [<Selector xpath='//c-tag[@id=4]/ancestor-or-self::*[position() = 3]' data=u'<a-tag class="a-class">A-text\n <b-tag'>] [<Selector xpath='//c-tag[@id=4]/ancestor-or-self::*[position() = 4]' data=u'<body><a-tag class="a-class">A-text\n '>]
attribute::
Ritorna gli attributi del nodo corrente
>>> for s in selector.xpath('//c-tag[@id=4]/attribute::*'): print s ... <Selector xpath='//c-tag[@id=4]/attribute::*' data=u'c-class'> <Selector xpath='//c-tag[@id=4]/attribute::*' data=u'4'>
child::
Ritorna tutti i figli de nodo corrente
>>> for s in selector.xpath('//b-tag[@id=1]/child::*'): print s ... <Selector xpath='//b-tag[@id=1]/child::*' data=u'<c-tag class="c-class" id="3">C-text</c-'> <Selector xpath='//b-tag[@id=1]/child::*' data=u'<c-tag class="c-class" id="4">C-text\n '>
child è sottinteso essere il nodo corrente quindi la sintassi
>>> for s in selector.xpath('//b-tag[@id=1]/child::c-tag'): print s ... <Selector xpath='//b-tag[@id=1]/child::c-tag' data=u'<c-tag class="c-class" id="3">C-text</c-'> <Selector xpath='//b-tag[@id=1]/child::c-tag' data=u'<c-tag class="c-class" id="4">C-text\n '> >>> for s in selector.xpath('//b-tag[@id=1]/c-tag'): print s ... <Selector xpath='//b-tag[@id=1]/c-tag' data=u'<c-tag class="c-class" id="3">C-text</c-'> <Selector xpath='//b-tag[@id=1]/c-tag' data=u'<c-tag class="c-class" id="4">C-text\n '>
descendant::
Ritorna tutti i nodi discendenti del noto corrente (figli, figli dei figli ecc)
>>> for s in selector.xpath('//b-tag[@id=1]/descendant::*'): print s ... <Selector xpath='//b-tag[@id=1]/descendant::*' data=u'<c-tag class="c-class" id="3">C-text</c-'> <Selector xpath='//b-tag[@id=1]/descendant::*' data=u'<c-tag class="c-class" id="4">C-text\n '> <Selector xpath='//b-tag[@id=1]/descendant::*' data=u'<d-tag>D-text</d-tag>'>
descendant-or-self::
Ritorna tutti i discendenti del nodo corrente, partendo dal nodo stesso (position())
>>> for pos in range(1, 4): ... r.xpath('//root/descendant-or-self::*[position()= %s]' % pos) ... [<Selector xpath='//root/descendant-or-self::*[position()= 1]' data=u'<root><foo><bar attr="x"></bar></foo><fo'>] [<Selector xpath='//root/descendant-or-self::*[position()= 2]' data=u'<foo><bar attr="x"></bar></foo>'>] [<Selector xpath='//root/descendant-or-self::*[position()= 3]' data=u'<bar attr="x"></bar>'>]
following::
Ritorna tutto ciò che nel documento viene dopo il tag di chiusura del nodo corrente:
>>> for s in selector.xpath('//b-tag[@id=1]/following::*'): print s ... <Selector xpath='//b-tag[@id=1]/following::*' data=u'<b-tag id="2">B-text\n <c-tag id="'> <Selector xpath='//b-tag[@id=1]/following::*' data=u'<c-tag id="5">C-text\n <d-tag>'> <Selector xpath='//b-tag[@id=1]/following::*' data=u'<d-tag>D-text</d-tag>'> <Selector xpath='//b-tag[@id=1]/following::*' data=u'<d-tag id="6">D-text\n <e-'> <Selector xpath='//b-tag[@id=1]/following::*' data=u'<e-tag>E-text</e-tag>'> <Selector xpath='//b-tag[@id=1]/following::*' data=u'<e-tag>E-text\n <f-tag'> <Selector xpath='//b-tag[@id=1]/following::*' data=u'<f-tag>F-text</f-tag>'> <Selector xpath='//b-tag[@id=1]/following::*' data=u'<b-tag attr="b-attr">B-text\n <c-t'> <Selector xpath='//b-tag[@id=1]/following::*' data=u'<c-tag>C-text\n <d-tag>D-text<'> <Selector xpath='//b-tag[@id=1]/following::*' data=u'<d-tag>D-text</d-tag>'>
se vogliamo filtrare il risultato ad esempio solo i ‘c-tag’ successivi:
>>> for s in selector.xpath('//b-tag[@id=1]/following::c-tag'): print s ... <Selector xpath='//b-tag[@id=1]/following::c-tag' data=u'<c-tag id="5">C-text\n <d-tag>'> <Selector xpath='//b-tag[@id=1]/following::c-tag' data=u'<c-tag>C-text\n <d-tag>D-text<'>
following-sibling::
Ritorna tutti i fratelli (siblings, cioè i nodi sullo stesso ASSE) dopo il nodo corrente:
>>> for s in selector.xpath('//b-tag[@id=1]/following-sibling::*'): print s ... <Selector xpath='//b-tag[@id=1]/following-sibling::*' data=u'<b-tag id="2">B-text\n <c-tag id="'> <Selector xpath='//b-tag[@id=1]/following-sibling::*' data=u'<b-tag attr="b-attr">B-text\n <c-t'>
parent::
Ritorna il genitore del nodo corrente
>>> selector.xpath('//b-tag[@id=1]/parent::*') [<Selector xpath='//b-tag[@id=1]/parent::*' data=u'<a-tag class="a-class">A-text\n <b-tag'>]
preceding::
Ritorna tutto ciò che sta prima del tag di apertura del nodo corrente:
>>> for s in selector.xpath('//b-tag[@id=1]/preceding::*'): print s ... <Selector xpath='//b-tag[@id=1]/preceding::*' data=u'<b-tag class="b-class">B-text</b-tag>'>
preceding-sibling::
Ritorna tutti i fratelli (sibling) prima del nodo corrente
>>> for s in selector.xpath('//b-tag[@id=1]/preceding-sibling::*'): print s ... <Selector xpath='//b-tag[@id=1]/preceding-sibling::*' data=u'<b-tag class="b-class">B-text</b-tag>'>
self::
Ritorna il nodo corrente
>>> selector.xpath('//b-tag[@id=1]/self::*') [<Selector xpath='//b-tag[@id=1]/self::*' data=u'<b-tag id="1">B-text\n <c-tag clas'>]
Riassumendo:
1. I percorsi possono essere ASSOLUTI:
>>> for s in selector.xpath('body/a-tag/b-tag/c-tag'): print s ... <Selector xpath='body/a-tag/b-tag/c-tag' data=u'<c-tag class="c-class" id="3">C-text</c-'> <Selector xpath='body/a-tag/b-tag/c-tag' data=u'<c-tag class="c-class" id="4">C-text\n '> <Selector xpath='body/a-tag/b-tag/c-tag' data=u'<c-tag id="5">C-text\n <d-tag>'> <Selector xpath='body/a-tag/b-tag/c-tag' data=u'<c-tag>C-text\n <d-tag>D-text<'>
2. I percorsi possono essere RELATIVI:
>>> for s in selector.xpath('//c-tag/../..'): print s ... <Selector xpath='//c-tag/../..' data=u'<a-tag class="a-class">A-text\n <b-tag'>
3. La sintassi Xpath formale è la seguente:
axisname::nodetest[predicate]
link utili:
parte 2. xpath: Abbraviazioni
parte 3. xpath: Funzioni generiche
scrapy
xpath syntax
Commenti recenti