Fondamenti di Programmazione

Documenti Strutturati

La nozione degli alberi vista finora e' utile per descrivere documenti con formattazione. In questa lezione introdurremo l'HTML, che e' il formato di codifica dei dati che si usa per le pagine web. Troverete esempi si HTML in tutto il web, ma per questa lezione abbiamo preparato alcuni esempi semplici che trovate nel pacchetto insieame a funzioni utile per caricare i file in memoria.

Formato HTML

Finora abbiamo visto come codificare un documento salvandolo direttamente su file di testo. Quello che vorremmo aggiungere e' la possibilita' di marcare parti del file con attributi per indicare, ad esempio, testo che appartiene ad un titolo o che va enfatizzato. Per fare questo, dobbiamo definire uin formato in modo che vari programmi possano interagire scambiandosi questi dati. Ci sono una larga varieta' di formati per questo scopo, ma attualmente il puo' usato e' l'HTML che si usa principalmente nelle pagine web e per gli ebooks, cioe' libri elettronici. Lo scopo di questo lezione non e' di essere completa sull'HTML, ma di darne le basi e mostrare come si puo' elaborare l'HTML in Python. Per una lista completa consultate qui.

L'HTML e' un formato di testo che usa codici di inizio/fine per indicare parti del documento con propreita' diverse. Ad esempio, un paragrafo e' indicato con

<p>Questo e' un paragrafo.</p>

dove <p> indica l'inizio del paragrafo e </p> la fine. Per indicare due paragrafi consecutivi, basta aggiungere un'altro paragrafo facendo

<p>Questo e' un paragrafo.</p>
<p>Questo e' un'altro paragrafo.</p>

Per marcare del testo da enfatizzare, si puo' inserire nel paragrafo un'altra sequenza di inizio/fine del tipo

<p>Questo e' un <em>paragrafo</em>.</p>

Infine possiamo includere anche immagini in un documento inserendo un elemento HTML con delle proprieta'. Ad esempo, per insirire l'immagine image.png possiamo usare:

<img src="image.png">

Struttura dei Documenti HTML

Un documento HTML e' strutturato come un albero di nodi, ognuno dei quali puo' contenere altri nodi, testo o non contenere nulla. Nel formato ogni nodo e' inizia con un tag di inizio, formato mettendo il tipo del nodo tra i caratteri < e >. Il nodo si chiude con un tag di chiusura che include il carattere /. I nodi possono avere attributi per specificarne il comportamento. Gli attributi si indica nel tag di apertura con il loro nome seguito da = e una stringa per il valore. Gli attributi sono opzionali. Il formato generale di un nodo e' quindi:

<tag attributo="valore">contenuto</tag>

I tag che non hanno contenuto non devono essere chiusi, come nell'esempio per img prima. Il contenuto di un nodo e' una lista di altri nodi. Se un nodo continere solo testo, il testo viene messo direttamente nel formato. L'esempio precedente dei paragrafi e' utile.

In generale, la struttura completa di un documento HTML e' del tipo

<html>
<head>
    <!-- attributi del documento, come titolo, autore, etc. -->
</head>
<body>
    <!-- contenuto del documento che viene visualizzato -->
</body>
</html>

Tutti i browser moderni moderni accettano anche frammenti di HTML, cioe' la parte contenuta nel body. Ma per fare processing due documenti sul web si deve assumere che il documento sia completo.

Tag Comuni

L'HTML definisce molti tag per marcare il documento. Qui sotto listiamo i piu' comuni.

Un esempio di un documento HTML semplice e' listato di seguito. La pagina generata si puo' vedere da questo link.

<html>
<body>
    <h1>Un Semplice Documento</h1>
    <p>Un paragrago con testo
        <em>enfatizzato</em> e 
        <strong>molto enfatizzato</strong>.
    </p>
    <p>Segue il logo dell'
    <a href="http://en.wikipedia.org/wiki/HTML5">HTML 5</a>.</p>
    <img src="img_logo.png">
</body>
</html>

Rappresentazione ad Albero di Documenti

Ci sono vari modi per manipolare l'HTML. In questa lezione, caricheremo la rappresentazione testuale HTML in un albero, usando la libreria allegata al pacchetto. Lavoreremo poi su quest'albero modificandolo, per poi riscriverlo in testo. Per caricare un file HTML usiamo la funzione html.parse(text,classname) che prende in input il testo text e il nome classname della classe.

L'HTML e' rappresentato come un albero di nodi, ognuno dei quali ha un tag, un dizionario di proprieta' attr, una lista di nodi contenuti content e una variabile closed che indica se il nodo va chiuso. Per semplificare il codice, il testo e' contenuto in un nodo con tag fittizio _text_ dove il contenuto e' una stringa (sequenza di caratteri). Per controllare se un nodo e' testo, si puo' usare il metodo istext. Possiamo "stampare" l'HTML in una string usando il metodo ricorsivo di seguito.

class HTMLNode(object):
    def __init__(self,tag,attr,content,closed=True):
        self.tag = tag
        self.attr = attr
        self.content = content
        self.closed = closed

    def istext(self):
        return self.tag == '_text_'

    def tostring(self):
        if self.istext(): 
            return self.content
        ret = '<'+self.tag
        for k, v in self.attr.items():
            ret += ' '+k+'="'+v+'"'
        ret += '>'
        if self.closed:
            for c in self.content:
                ret += c.tostring()
            ret += '</'+self.tag+'>'
        return ret

with open('page_simple.html','rU') as f:
    doc = html.parse(f.read(),HTMLNode)
print doc.tostring()

Il codice precedente stampa

<html>
<body>
    <h1>Un Semplice Documento</h1>
    <p>Un paragrago con testo
        <em>enfatizzato</em> e 
        <strong>molto enfatizzato</strong>.
    </p>
    <p>Segue il logo dell'
    <a href="http://en.wikipedia.org/wiki/HTML5">HTML 5</a>.</p>
    <img src="img_logo.png">
</body>
</html>

Operazioni sui Documenti

Possiamo ora scrivere una funziona che lista tutti gli elementi con un certo tag. Questa funzione puo' servire ad esempio per listare tutti i link contenuti nella pagina.

class HTMLNode(object):
    ...
    def find_by_tag(self,tag):
        ret = []
        if self.tag == tag: ret += [self]
        if not self.istext():
            for c in self.content:
                ret += c.find_by_tag(tag)
        return ret

for link in doc.find_by_tag('a'):
    print link.attr
print ''

Il codice precedente stampa

{'href': 'http://en.wikipedia.org/wiki/HTML5'}

Possiamo anche fare una funziona che modifica l'abero. Ad esempio possiamo eliminare i nodi con un certo tag.

class HTMLNode(object):
    ...
    def remove_by_tag(self,tag):
        if self.istext(): return
        ncontent = []
        for c in self.content:
            if c.tag == tag: ncontent += c.content
            else: 
                c.remove_by_tag(tag)
                ncontent += [c]
        self.content = ncontent

doc.remove_by_tag('a')
print doc.tostring()

doc.remove_by_tag('img')
print doc.tostring()

Il codice precedente stampa

<html>
<body>
    <h1>Un Semplice Documento</h1>
    <p>Un paragrago con testo
        <em>enfatizzato</em> e 
        <strong>molto enfatizzato</strong>.
    </p>
    <p>Segue il logo dell'
    HTML 5.</p>
    <img src="img_logo.png">
</body>
</html>

<html>
<body>
    <h1>Un Semplice Documento</h1>
    <p>Un paragrago con testo
        <em>enfatizzato</em> e 
        <strong>molto enfatizzato</strong>.
    </p>
    <p>Segue il logo dell'
    HTML 5.</p>

</body>
</html>