HtmlUnit

Estrarre informazioni dal webdi

HtmlUnit è un framework che affonda le sue radici in JUnit, un framework per i test di unità per linguaggio Java. L‘esperienza con JUnit è risultata fondamentale per far crescere la metodologia di "sviluppo guidato dai test", utilissima in fase di progettazione e di implementazione.

Introduzione

Negli ultimi anni abbiamo assistito a una forte espansione del web e delle informazioni in esso contenute. Gli utenti utilizzano quindi il web come una fonte inesauribile di informazioni, dal quale estrarre solo quelle di interesse specifico: infatti le informazioni presenti spaziano su numerosi argomenti e presentano diversi gradi di rilevanza ai fini della ricerca dell'utente. In particolare, alcune di queste informazioni vengono classificate in letteratura come informazioni "rumorose" [1]: con questa accezione si intendono quelle informazioni che non sono pertinenti con il contenuto informativo della pagina web, ma si traducono infatti in banner pubblicitari, intestazioni e note a piè di pagina, plug-in e altro ancora. Questo tipo di "rumore" è presente ovunque nel web, in quanto produce vantaggi economici per fornitori di servizi web, aziende pubblicitarie e sponsor in genere. Purtroppo, però, il rumore si distanzia molto da ciò che l'utente cerca nel web e spesso costituisce addirittura fattori di disturbo alla navigazione lineare.
Inoltre, c'è da considerare che la sempre crescente espansione del web ha reso necessaria l'ideazione di uno strumento capace di reperire informazioni selezionate in relazione alla pertinenza con l'argomento di ricerca delle informazioni: un sistema di questo genere presenterebbe, quindi, importanti problemi di prestazioni in termini di "precision" e "recall" [1] nel caso in cui dovesse memorizzare intere pagine web, comprensive di tutto il rumore che hanno incorporato.
La soluzione a questo problema è stata proposta negli ultimi anni attraverso lo sviluppo di processi basati sulla suddivisione delle pagine web in blocchi semanticamente omogenei, eliminando quindi quelli di più bassa pertinenza.
Questo compito risulta, però, non troppo semplice da mettere in pratica, proprio perchè le pagine web sono pensate, e quindi strutturate, per il consumo da parte di utenti umani attraverso l'utilizzo di browser web: per questo motivo, risultano spesso difficilmente processabili da programmi che girano su calcolatori. Un apporto importante al corretto funzionamento di questi strumenti, quindi, sarebbe l'estrazione e la conservazione in locale delle informazioni reperite, in modo da elaborarle e analizzare, per poi riproporle, integrandole magari con altre informazioni: ma non sempre queste operazioni risultano fattibili.
Gli strumenti utilizzati per l'estrazione delle informazioni dal web vengono chiamati wrapper (si veda in particolare [3] e [4]): il loro lavoro consiste nella suddivisione della pagina web basandosi sulla costruzione di una sua rappresentazione ad albero DOM (Documet Object Model), come rappresentato in figura 1

Figura 1 - Esempio di costruzione di albero DOM di una generica pagina web partendo dai tag HTML

In particolare, essi svolgono in sequenza i seguenti compiti:

  • effettuano il parsing di una pagina web (X/HTML), laddove il parser è uno strumento in grado di interpretare un particolare linguaggio (in questo caso X/HTML) e individuare le informazioni in esso contenute;
  • estraggono i dati di interesse;
  • riversano i dati in un altro formato elettronico (XML, RDBMS, etc.).

Come visibile nell'esempio della figura 1, l'albero DOM fa riferimento alla struttura sintattica della pagina HTML e cerca quindi di identificare i vari sotto-alberi facendo riferimento ai tag più usati come , <TABLE>, <TR>, <TD>, <P>, <UL>. </p> <p> Un wrapper effettua quindi una "ricerca automatica", agevolando la ricerca con l'eliminazione delle pagine che non contengono informazioni di interesse alla ricerca stessa: questo comporta una notevole agevolazione in consumo di banda per la trasmissione della lista di risultati ottenuti, ad esempio, dalla ricerca su un motore di ricerca. In questo modo, il wrapper diviene un filtro per le informazioni, navigando la pagina prima della sua effettiva visualizzazione, ricercando le informazioni utili ed eliminando i "rumori". <br /> Un wrapper può inoltre assumere un'importanza vitale per la navigazione di Internet da parte di persone affette da qualche tipo di handicap: ad esempio, una persona daltonica potrebbe utilizzare la piattaforma per analizzare preventivamente i contenuti delle pagine HTML e sostituire i colori non riconosciuti con altri, permettendogli quindi una migliore fruizione del contenuto.</p> <h3>Un framework per i test unitari per pagine HTML</h3> <p>In questo contesto si inserisce HtmlUnit [2], un framework Java per il test di applicazioni web-based. </p> <p> Concettualmente, risulta molto simile al framework HttpUnit, ma in realtà l'implementazione dei due risulta molto diversa: infatti HttpUnit lavora principalmente con i protocolli HTTP, quindi può offrire le funzionalità per gestire request e response; HtmlUnit, invece, modella il documento generato dal browser, in modo da poter lavorare con le informazioni contenute in esso.</p> <h3>Come utilizzare il framework</h3> <p>La prima operazione da effettuare è la creazione di una classe che si occupa di interrogare la pagina HTML richiesta: questo può essere realizzato mediante l'istanza di un oggetto</p> <pre>com.gargoylesoftware.htmlunit.WebClient</pre> <p>In questo primo esempio, creiamo un client Web e gli facciamo caricare il sito di MokaByte.<br /> Verificheremo che questa pagina abbia il corretto titolo. È importante notare che il metodo getPage() può restituire differenti tipi di pagine in funzione contenuto del wrapper. In questo caso ci aspettiamo un content type text/html, infatti faremo un cast sul risultato attraverso la classe</p> <pre>com.gargoylesoftware.htmlunit.html.HtmlPage.<br>public void testHomePage() throws Exception <br>{<br> final WebClient webClient = new WebClient();<br> final HtmlPage page <br> = (HtmlPage)webClient.getPage("http://www.mokabyte.it");<br> <br> if (page.getTitleText().equals("MokaByte"))<br> System.out.println("Test POSITIVO");<br> else<br> System.out.println("Test NEGATIVO");<br>}</pre> <p>HtmlUnit è uno strumento molto flessibile e permette di verificare le caratteristiche cross-browsing dell'applicazione da testare: utilizzando la classe</p> <pre>com.gargoylesoftware.htmlunit.BrowserVersion</pre> <p>nel costruttore del WebClient è infatti possibile specificare un determinato browser.</p> <p> Il framework fornisce il supporto per i più comuni browser, ma esiste la possibilità di crearne uno custom (attraverso l'istanziazione della classe BrowsingVersion).</p> <pre>public void testHomePage() throws Exception {<br>final WebClient webClient = new WebClient(BrowserVersion.FIREFOX_2);<br>final HtmlPage page<br>= (HtmlPage)webClient.getPage("http://www.mokabyte.it");<br>if (page.getTitleText().equals("MokaByte"))<br>System.out.println("Test POSITIVO");<br>else<br>System.out.println("Test NEGATIVO");<br>}</pre> <p> </p> <p>Specificando la costante BrowserVersion, cambierà il campo "user agent header" presente nella richiesta HTTP che viene inviata al server e pertanto cambieranno alcune caratteristiche del comportamento dei JavaScript.</p> <p> Molto spesso accade di dover utilizzare il browser passando le richieste HTTP attraverso un proxy. Il costruttore della classe WebClient permetter di specificare il server proxy per ovviare a questo problema.</p> <pre>public void testHomePage() throws Exception {<br> final WebClient webClient <br> = new WebClient(BrowserVersion.FIREFOX_2,<br> "http://myproxyserver", 8080);<br> final HtmlPage page <br> = (HtmlPage)webClient.getPage("http://www.mokabyte.it");<br> <br> if (page.getTitleText().equals("MokaByte"))<br> System.out.println("Test POSITIVO");<br> else<br> System.out.println("Test NEGATIVO");<br>}</pre> <p>Come si può vedere, nel caso in cui si stia utilizzando un proxy, è necessario specificare sia la versione del browser che l'indirizzo, o il nome, del proxy server e la relativa porta.</p> <p> Il risultato restituito dal metodo getPage() consiste in un oggetto HtmlPage, su cui è possibile effettuare diverse operazioni. Ad esempio, se nella pagina esiste una form, può essere inviata impostando i relativi parametri con il seguente metodo:</p> <pre>public void submitForm(HtmlPage page,String inputName, String inputValue) {<br> HtmlForm form = page.getFormByName("myform");<br> HtmlSubmitInput button <br> = (HtmlSubmitInput) form.getInputByName("submitButton");<br> HtmlTextInput textField <br> = (HtmlTextInput) form.getInputByName(inputName);<br> textField.setValueAttribute(inputValue);<br> HtmlPage pageResult = (HtmlPage) button.click();<br> }</pre> <p>Di particolare rilevanza il supporto fornito da HtmlUnit per JavaScript; vediamo qualche esempio: utilizzare Document.write()</p> <p> Sappiamo che avere una pagina contenente javascript significa che il contenuto della pagina può cambiare dinamicamente. Per esempio il seguente frammento HTML genererà dinamicamente cinque campi di testo e li inserirà all'interno di una tabella. Ogni campo avrà un nome univoco creato dalla giustapposizione dell'indice alla stringa "textfield".</p> <pre><html><head><title>Table sample





Vorremmo che il test verifichi la creazione dei cinque campi di testo; segue il codice implementato attraverso HtmlUnit che risolve il problema posto.

public void testDocumentWrite() throws Exception {
final WebClient webClient = new WebClient();
final HtmlPage page = (HtmlPage)webClient.getPage(
new URL( "http://myserver/test.html" ) );
final HtmlForm form = page.getFormByName("form1");
for( int i=1; i<= 5; i++ ) {
final String expectedName = "textfield"+i;
assertEquals(
"text",
form.getInputByName(expectedName).getTypeAttribute());
}
}

Vediamo ora come effettuare il parsing di una pagina web contenente una tabella. Negli esempi che seguono faremo riferimento alla seguente pagina HTML.

Tabella









Numero Descrizione
1 Bicicletta

Il frammento di codice che segue mostra come iterare fra le righe e le celle:

final HtmlTable table = (HtmlTable)page.getHtmlElementById("table1");
final List rows = table.getRows();
for (final Iterator rowIterator = rows.iterator(); rowIterator.hasNext(); ) {
final HtmlTableRow row = (HtmlTableRow) rowIterator.next();
System.out.println("Trovata una riga");
final List cells = row.getCells();
for (final Iterator cellIterator
= cells.iterator(); cellIterator.hasNext();) {
final HtmlTableCell cell = (HtmlTableCell) cellIterator.next();
System.out.println(" Trovata una cella: " + cell.asText());
}
}

Il codice di esempio che segue mostra come accedere a una specifica cella attraverso numero di riga e colonna.

final WebClient webClient = new WebClient();
final HtmlPage page
= (HtmlPage)webClient.getPage( new URL("http://foo.com") );
final HtmlTable table = (HtmlTable)page.getHtmlElementById("table1");
System.out.println( "Cell (1,2)="+ table.getCellAt(1,2);

Conclusioni

In questo primo articolo è stata presentata una breve panoramica sulla teoria dell'estrazione delle informazioni e del testing di applicazioni web-oriented. Abbiamo infine imparato a utilizzare il framework HtmlUnit attraverso degli esempi di difficoltà crescente. Per ulteriori approfondimenti si rimanda alla bibliografia a corredo, soprattutto alla documentazione presente sul sito web ufficiale del progetto [2].

Riferimenti

[1] C. Biancalana - F. Gasparetti - A. Micarelli, "Intelligent Search on the Internet", in O. Stock - M. Schaerf (eds.), "Reasoning, Action and Interaction in AI Theories and Systems", LNAI Festschrift Volume 4155 of the Lecture Notes in Computer Science series, Springer, Berlin, 2006.

[2] HtmlUnit
http://htmlunit.sourceforge.net/

[3] Lang Yi - Bing Liu - Xiaoli Li, "Eliminating Noisy Information in Web Pages for Data Mining". SIGKDD '03, August 24-27, 2003. ACM, 2003.

[4] Sandip Debnath - Prasenjit Mitra - C. Lee Giles, "Automatic Extraction of Informative Blocks from Webpages". SAC '05, March 13-17, 2005, Santa Fe, New Mexico, USA. ACM, 2005.

Condividi

Pubblicato nel numero
126 febbraio 2008
Luana Rinaldi è nata a Napoli nel 1980. Si è laureata in Ingegneria Informatica presso l‘Università degli studi di Roma Tre, portando avanti una tesi di ricerca in bioinformatica. Dal 2000 si occupa professionalmente di analisi, progettazione e sviluppo di applicazioni in linguaggio Java con una particolare attenzione alle tecnologie…