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