Python: Html Parsing veloce ed intuitivo
Link sponsorizzati
Articolo a cura di Malex
Vi è mai capitato di dover parsare una pagina HTML?
Se sì, vi sarete resi conto che è da pazzi provare a farlo con le regex, e comunque, molti parser che si trovano usano dei metodi a volte cervellotici per gestire l’html una volta parsato. Inoltre, in genere richiedono il parsing di tutto il file HTML. Ma se a me bastasse arrivare fino ad un certo tag e poi smettere? Dovrei sacrificare un relativo aumento di prestazioni?
Link sponsorizzati
Logicamente no, e ci viene in aiuto la classe HTMLParser, presente nella libreria standard di Python.
Tale classe è presente in librerie diverse a seconda della vostra versione di Python: in Python 2.X esso si trova, appunto, nel modulo HTMLParser; in Python 3.X esso si trova nel modulo html (che contiene anche altre feature che si trovavano precedentemente in altri moduli); più precisamente nel sottomodulo parser (quindi, alla fine, html.parser)
Per questo esempio utilizzero Python 3.1, se avete Python 2.X, basterà cambiare il richiamo al modulo html.parser con HTMLParser.
#import HTMLParser
#decommentare la seconda linea e commentare la prima per Python 2.X
class MyParser(HTMLParser.HTMLParser):
def __init__(self,data):
HTMLParser.HTMLParser.__init__(self)
self.f=open("./urls.txt",'a+')
self.feed(data)
def handle_starttag(self,tag,attrs):
if tag=="link":
for x,y in attrs:
if x.lower()=="href":
print("Abbiamo trovato un link a",y,"esso verrà memorizzato nel file ./urls.txt",sep=" ")
self.f.write(y)
elif tag=="img":
count=0
for i,j in attrs:
if i.lower()=="src":
print("An image with path ",j," was found")
count+=1
print("A total of ",str(count)," image was found")
Ok, questo codice non è molto bello, ma rende l’idea, vediamo cosa facciamo nello specifico: ereditiamo la classe base HTMLParser. Ridefiniamo il costruttore, facendo sì che riceva in input come argomento la nostra stringa html. A questo punto chiamiamo il costruttore della classe parent, che ci inizializzerà metodi e variabili. Poi, giacché abbiamo messo la ricezione dei dati direttamente nel costruttore, dobbiamo chiamare il metodo feed (ereditato dalla parent) che passa al nostro parser la stringa html: a questo punto, il parsing parte, ed esso usa un funzionamento tramite un sistema a wrapping (riproducibile con gli @ decorators, di cui parlerò in un prossimo articolo).
In pratica, quando viene trovato un tag HTML, viene chiamata la funzione handle_starttag, che nella classe base non esegue assolutamente niente, ma è stata creata appositamente per essere sovrascritta. Essa ha come parametri 2 valori: tag contiene il nome del tag (senza < e >) e attrs, una lista contenente una serie di tuple (nome_attributo, valore). L’esempio da me mostrato, printa a schermo quando viene trovata un’immagine o un link, se è un link ne salva il href su un file, se è un’immagine aumenta il suo count, ed alla fine mostra il totale.
Questo esempio è molto idiota, ma mostra l’utilizzo più semplice ed immediato di questa classe, ossia il parsing dell’inizio di un tag html e relativi attributi.
La classe base offre anche altri utili metodi overloadabili: handle_endtag, che viene chiamato quando viene incontrato un tag di chiusura; handle_startendtag, il quale viene chiamato per i tag XHTML-style vuoti (tipo < img src="something" />); handle_data, chiamato per tutto ciò che non è un tag, handle_charref, per un riferimento con un carattere del tipo ref; , quali le virgolette singole, in certi casi; handle_entityref, per caratteri del tipo &name; come ad esempio > per >; handle_comment, quando viene trovato un commento html (ossia ); handle_decl per dichiarazioni del tipo < ! >; handle_pi per le istruzioni di processo, tipo < ?proc some='thing'>. Si rimanda alla documentazione ufficiale di Python per maggiori dettagli.
Ci sono altri metodi molto utili però: getpos, che restituisce la posizione attuale, con numero di linea ed offset; get_starttag_text che restituisce il testo presente nel tag aperto più recentemente (si consiglia di usarlo sugli endtag).
Inoltre si possono utilizzare i metodi: feed, già visto ed utilizzato nella classe di esempio, ha la funzione di inviare i dati al parser; close, metodo forza il parsing dei dati, ed è in genere utilizzato per aggiungere istruzioni da eseguire alla fine del parsing stesso.
Qualora il parsing non avesse buon fine, verrà sollevata un’eccezione HTMLParserError, la quale ci da, oltre ad un messaggio che tenta di spiegarci il motivo del parsing non riuscito, anche il numero di riga ed offeset dove è avvenuto l’errore.
Spero di esservi stato utile, alla prossima, ed aspetto ansioso vostri commenti!
Link sponsorizzati


Hai scritto solo due articoli su Python, ed entrambi mi sono stati utilissimi!!
Sembrano scritti ad-hoc per i problemi che incontro
Grazie, aggiungo il tuo blog ai feed
Contento di esserti utile. è in preparazione un nuovo articolo in Python, Solo per motivi tecnici non sono ancora riuscito a pubblicarlo, arriverà al più presto, salvo nuovi incovenienti entro mercoledì sera.
Ciao intanto complimenti hai scritto un ottimo articolo. Io dovrei realizzare un parser html per selezionare solo alcune porzioni di codice correggere eventuali errori e inserire il tutto in un nuovo file XML pulito e che rispetti gli standard per la validazione w3c. Leggendo il tuo articolo l’ho trovato molto interessante come inizio, solo che non ho mai usato python, volevo chiederti un consiglio.. Mi conviene avventurarmi in questo nuovo linguaggio per una cosa del genere oppure no? Che ne pensi?
Beh.. dipende. Conosci altri linguaggi? Se si è molto probabile che sia possibile fare la stessa cosa usando anche un linguaggio a te noto (senza necessariamente dovertene imparare un altro). Tuttavia in linea di massima puoi sfruttare anche il Python senza problemi, non è un linguaggio difficile.