Home > python, scrapy, xpath > scrapy: appunti

scrapy: appunti

12 Ottobre 2017

Installazione

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

Ora per vedere che tutto funzioni, è sufficiente eseguire il comando:

scrapy shell
...
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    <scrapy.crawler.Crawler object at 0x04108D90>
[s]   item       {}
[s]   settings   <scrapy.settings.Settings object at 0x04108E90>
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                  Fetch a scrapy.Request and update local objects
[s]   shelp()           Shell help (print this help)
[s]   view(response)    View response in a browser

per uscire:

>>>exit()

Progetto

Creiamo il primo progetto, ad esempio un estrattore di ultime notizie dell’ANSA:

scrapy startproject ansa

prima però di scrivere codice, prendiamo confidenza con i principali comandi.
Lanciamo la shell di scrapy dandole in pasto la pagina web che ci interessa:

scrapy shell "www.ANSA.it"
...
[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    <scrapy.crawler.Crawler object at 0x04035DB0>
[s]   item       {}
[s]   request    <GET http://www.ANSA.it>
[s]   response   <200 http://www.ANSA.it>
[s]   settings   <scrapy.settings.Settings object at 0x04035EF0>
[s]   spider     <DefaultSpider 'default' at 0x4302fb0>
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                  Fetch a scrapy.Request and update local objects
[s]   shelp()           Shell help (print this help)
[s]   view(response)    View response in a browser
...

come si nota ci vengono messi a disposizione alcuni oggetti, tra i quali ‘response’, sul quale è
possibile effettuare query di ogni tipo. Si possono usare sia il metodo css() che il metodo xpath().
Ad es. voglio ottenere il titolo della pagina:

>>> response.css('title')
[<Selector xpath=u'descendant-or-self::title' data=u'<title>ANSA.it</title>'>]
>>> response.xpath('//title')
[<Selector xpath='//title' data=u'<title>ANSA.it</title>'>]

Nel caso di utilizzo del metodo css() è previsto l’utilizzo di una sintassi dedicata.
Il metodo css() comunque ritornerà sempre un oggetto selector che si rifarà alla sintassi
di Xpath:

>>> response.css('title')
[<Selector xpath=u'descendant-or-self::title' data=u'<title>ANSA.it</title>'>]
>>> response.css('title::text')
[<Selector xpath=u'descendant-or-self::title/text()' data=u'ANSA.it'>]
>>> response.css('title::text').extract_first()
u'ANSA.it'

Selector

Un selector altro non è che un’istanza della classe Selector, alla quale viene passata una stringa
o un oggetto TextResponse e, a seconda del dato in ingresso sceglie il tipo di parsing
(XML o HTML) da usare:

>>> from scrapy import Selector
>>> body = "<foo><bar>Some text</bar></foo>"
>>> selector = Selector(text=body)
>>> selector.xpath('//bar[text()]').extract()
[u'<bar>Some text</bar>']
>>> selector.xpath('//bar/text()').extract()
[u'Some text']

Xpath è il linguaggio che permette di selezionare i nodi in un documento XML e può essere utilizzato
anche per l’HTML.
come si nota, si passa una stringa html al selector e con il metodo extract(),
si ottiene una lista di testi che rispondono ai nostri criteri, o con extract_first()
si ottiene il primo della lista.
Attenzione ai path:
nel primo caso :’//bar’ troviamo il tag ed il testo contenuto da esso;
nel secondo caso: ‘//bar/text()’ troviamo solo il testo contenuto da quel tag!
Stesso principio per gli oggetti TextResponse:

>>> from scrapy.http import HtmlResponse
>>> resp = HtmlResponse(url='', body=body)
>>> Selector(response=resp).xpath('//bar[text()]').extract_first()
u'<bar>Some text</bar>'

>>> Selector(response=resp).xpath(‘//bar/text()’).extract_first()
u’Some text’

Xpath

Rispetto all’utilizzo dei Selector CSS, le espressioni Xpath sono più potenti
perchè oltre a navigare attraverso la struttura della pagina web, permettono di
osservare i contenuti dei nodi.
Qui è disponibile una serie di appunti su Xpath.

>>> response.xpath('//title[text()]').extract()
[u'<title>ANSA.it</title>']

Andiamo al sodo:
vogliamo ottenere una lista di tutti i selector che contengono articoli?
Per come è strutturata la pagina che stiamo analizzando, gli articoli sono racchiusi all’interno di tag “article”,
quindi:

>>> response.css('article').extract()
...
>>> response.xpath('//article').extract()
...

ovviamente per ottenere il primo della lista, basterebbe chiamare il metodo extract_first():

>>> response.css('article').extract_first()
...

Vediamo come ottenere i sunti delle singole notizie. Queste sono identificabili ad esempio dal tag

.
Quindi vogliamo filtrate tutti i tag

con attributo class=”pp-abs”:

>>> response.css('p.pp-abs').extract()
...

dove ‘p.pp-abs’ sta appunto per

Con la sintassi xpath (qui una breve guida sulla sintassi) sarebbe:

>>> response.xpath('//p[@class="pp-abs"]').extract()
...

Se volessimo solo i testi e non i tag contenitori:

>>> response.css('p.pp-abs::text').extract()
...
>>> response.xpath('//p[@class="pp-abs"]/text()').extract()
...

Oltre al sunto delle notizie, vogliamo ottenere i link delle stesse?
Per come è strutturata la pagina web (‘analizza sorgente pagina’ da browser…) potrei fare qualcosa del genere:

for r in response.css('article.news'):
    p = r.css('p::text').extract_first()
    href = r.css('a::attr(href)').extract_first()
    if p and href != "javascript:void(0);":
        print "%s\nlink: %s" % (p, href)

La condizione if è dovuta al fatto che non sempre all’interno del tag

è disponibile un un tag

e
quindi il metodo extract_first() ritornerebbe ‘None’ e inoltre molti href punterebbero al valore
“javascript:void(0);” che non vogliamo.

Con xpath invece:

for r in response.xpath('//article[@class="news"]'):
    p = r.xpath('descendant::p/text()').extract_first()
    href = r.xpath('descendant::a/@href').extract_first()
    if p and href != "javascript:void(0);":
        print "%s\nlink: %s" % (p, href)

Vediamo adesso come fare per memorizzare tutti i link delle notizie in anteprima ed aprirle tutte,
per memorizzarne il testo completo. La cosa si complica un po’, ma non troppo.
Per questo tipo di cosa utilizzeremo gli spiders.

link utili:
scrapy: spider
scrapy
xpath: appunti

Categorie:python, scrapy, xpath Tag: , ,
I commenti sono chiusi.