Autore Topic: [medio] Ottenere dati da una pagina HTML  (Letto 22754 volte)

Offline Dado

  • Nuovo arrivato
  • *
  • Post: 33
  • Respect: +7
    • Mostra profilo
  • Dispositivo Android:
    Motorola Milestone
  • Play Store ID:
    Edges Labs
[medio] Ottenere dati da una pagina HTML
« il: 29 Marzo 2010, 14:13:22 CEST »
+3
Livello di difficoltà: medio
Versione SDK utilizzata: 1.1
Link al file compresso del progetto eclipse: http://labs.edges.it/tutorial/HtmlParser.zip

In questo breve tutorial cercherò di illustrare nel modo più semplice possibile come effettuare il parsing di una pagina html e di conseguenza come ricavare dei dati da essa.
Per fare questo farò uso di una libreria opensource chiamata HtmlCleaner (disponibile al sito http://htmlcleaner.sourceforge.net/) ed inclusa nel file compresso del progetto.
Questa libreria non fa altro che "pulire" il codice html, questo perchè molte volte possono esserci errori nell'html (come per esempio tag non chiusi ecc.) i quali vengono "corretti" dai normali browser, ma ovviamente possono dare molti problemi quando bisogna creare un programma che interpreta del codice html, questa libreria quindi ci viene in aiuto generando del codice pulito.
Il funzionamento del programma è riassumibile nei seguenti passi:
1) pulizia del codice
2) caricamento in memoria del codice appena "ripulito"
3) analisi dello stesso alla ricerca delle parti che ci interessano
4) stampa tramite una TextView

Il progetto è formato da 2 classi, la prima HtmlParser è quella principale e come si può intuire dal nome è quella che si occupa di pulire ed analizzare la pagina web, la seconda classe chiamata con molta fantasia Finestra :P serve solamente a stampare il risultato dell'elaborazione.

Per semplicità (e anche per poco tempo disponibile :P) il programma non fa altro che ricevere come url il sito da analizzare e stampa tutti i link che trova nella pagina stessa.

Ora cercherò qui di seguito di focalizzarmi sulle parti più importanti:

Classe Finestra:
Codice (Java): [Seleziona]
HtmlParser hp = new HtmlParser(url_str);

/* passiamo il risultato alla TextView per visualizzarli. */
tv.setText(hp.Stampa(elementName));
Qua viene costruito l'oggetto HtmlParser passandogli come attributo l'url del sito che vogliamo analizzare, ed inseguito viene chiamata la funzione Stampa alla quale dobbiamo passare l'elemento di cui vogliamo avere le informazioni, nell'esempio l'emento è il tag <a> il quale indica un riferimento/link ad un oggetto/pagina.

Classe HtmlParser:

Il costruttore è abbastanza commentato quindi passerei a descrivere le altre 2 funzioni

Codice (Java): [Seleziona]
    List<Object> getElement(String elementName)
    {
        List<Object> elementList = new ArrayList<Object>();

        TagNode Elements[] = rootNode.getElementsByName(elementName, true);
        for (int i = 0; Elements != null && i < Elements.length; i++)
        {

            String type = Elements[i].getAttributeByName("href");
            if ( type != null )
            {
                elementList.add(Elements[i]);
            }

        }

        return elementList;
    }

Questa funzione non fa altro che scorrere l'html, caricato e "pulito" dal costruttore, fino a quando non trova gli elementi desiderati, in seguito controlla se è presente l'attributo href (quello che contiene il link) e se questo è presente lo aggiunge alla lista.

Codice (Java): [Seleziona]
public String Stampa(String elementName)
    {
        StringBuffer sb = new StringBuffer();
       
        try
        {
           
            List<Object> elementi = this.getElement(elementName);

            sb.append(">>> Stampa contenuto degli elementi '"+elementName+"' per il sito '"+url_str+"'\n");
            for (Iterator<Object> iterator = elementi.iterator(); iterator.hasNext();)
            {
                TagNode Element = (TagNode) iterator.next();
               
                sb.append("Link: " + Element.getAttributeByName("href").toString()+"\n");
               
               
            }
           
            return sb.toString();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
       
        return null;
    }

La funzione Stampa, come dice la parola stessa si occupa di generare una stringa di testo contenente tutti i link caricati nella lista.

Infine, non bisogna dimenticarsi di aggiungere il permesso per l'accesso ad Internet. Questa è la riga di codice da inserire nel file AndroidManifest.xml del vostro progetto.

Codice (XML): [Seleziona]
<uses-permission android:name="android.permission.INTERNET" />
Mi scuso se ci fossero errori di ortografia o di altro tipo ma il tempo a disposizione è poco :P

Bibliografia da cui ho preso spunto per il tutorial:
http://thinkandroid.wordpress.com/2010/01/05/using-xpath-and-html-cleaner-to-parse-html-xml/

Offline ChupaChups

  • Utente junior
  • **
  • Post: 78
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Google Nexus One
  • Play Store ID:
    fedepupo
  • Sistema operativo:
    Ubuntu/Windows 7
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #1 il: 29 Marzo 2010, 14:51:24 CEST »
0
ottimo, grazie mille
appena ho tempo lo provo, speriamo già in giornata  :-P

Offline giumazzi

  • Utente junior
  • **
  • Post: 54
  • Respect: +5
    • Mostra profilo
  • Dispositivo Android:
    acer liquid - android 2.1
  • Sistema operativo:
    windows xp - linux xubuntu - easy peasy
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #2 il: 07 Aprile 2010, 22:04:53 CEST »
0
Premetto che sono nuovo  a java e a android. Ho provato ad importare htmlparser in eclipse ma quando lo importo ricevo un errore. eclipse mi chiede di reimpostare il percorso delle classi (path builder). Come si deve organizzare il percorso delle classi per non avere questo errore

Offline ChupaChups

  • Utente junior
  • **
  • Post: 78
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Google Nexus One
  • Play Store ID:
    fedepupo
  • Sistema operativo:
    Ubuntu/Windows 7
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #3 il: 08 Aprile 2010, 00:58:22 CEST »
0
Dopo diversi giorni, finalmente ho avuto un momento libero e sono riuscito a provare il tutorial, perfetto, funziona!

però ho un problema, non voglio prendere gli href ma tutti gli elementi che hanno una determinata classe, come faccio?
grazie

Offline dami7net

  • Nuovo arrivato
  • *
  • Post: 15
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    HTC Hero
  • Sistema operativo:
    Mac OSX
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #4 il: 08 Aprile 2010, 01:41:32 CEST »
0
Semplice, sostituisci questa riga
Codice (Java): [Seleziona]
String type = Elements[i].getAttributeByName("href");
con queste altre due
Codice (Java): [Seleziona]
String type = Elements[i].getAttributeByName("class");
if (type="[i]classe desiderata[/i]"){
    // Codice da eseguire se ok
}else{
    // Codice da eseguire se false
}

Spero di aver capito la domanda e di essere stato utile!

Offline ChupaChups

  • Utente junior
  • **
  • Post: 78
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Google Nexus One
  • Play Store ID:
    fedepupo
  • Sistema operativo:
    Ubuntu/Windows 7
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #5 il: 08 Aprile 2010, 08:07:42 CEST »
0
Semplice, sostituisci questa riga
Codice (Java): [Seleziona]
String type = Elements[i].getAttributeByName("href");
con queste altre due
Codice (Java): [Seleziona]
String type = Elements[i].getAttributeByName("class");
if (type="[i]classe desiderata[/i]"){
    // Codice da eseguire se ok
}else{
    // Codice da eseguire se false
}

Spero di aver capito la domanda e di essere stato utile!

ho provato così, ma non mi stampa nulla :(
non capisco.. mmmm :-(

EDIT: forse ho capito perchè non mi stampa nulla
negli href stampa il cotnenuto di href="XXXXXX" quindi XXXXXX
ma se io ho l'html fatto così:
<span class="miaclasse">YYYYY</span>
come faccio a stampare YYYYY?????

grazie
« Ultima modifica: 08 Aprile 2010, 14:29:16 CEST da ChupaChups »

Offline .zero

  • Nuovo arrivato
  • *
  • Post: 8
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    N/A
  • Sistema operativo:
    Archlinux, Windows 7
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #6 il: 08 Aprile 2010, 22:33:29 CEST »
0
ho provato così, ma non mi stampa nulla :(
non capisco.. mmmm :-(

EDIT: forse ho capito perchè non mi stampa nulla
negli href stampa il cotnenuto di href="XXXXXX" quindi XXXXXX
ma se io ho l'html fatto così:
<span class="miaclasse">YYYYY</span>
come faccio a stampare YYYYY?????

grazie

Richiede qualche modifica al codice iniziale (anche per semplificarlo, molto del codice di sopra è inutile se ho ben capito la javadoc di htmlcleaner).
Ti conviene aggiungere un nuovo metodo, così eviti di toccacciare troppo :-P

Codice (Java): [Seleziona]
public String StampaClasse(String className) {
    StringBuffer sb = new StringBuffer();

    try {  
        List<TagNode> elementi = rootnode.getElementListByAttValue("class", className);

        sb.append(">>> Stampa contenuto degli elementi con classe'"+className+"' per il sito '"+url_str+"'\n");
        TagNode el;
        Iterator<TagNode> iterator = elementi.iterator();
        while(iterator.hasNext()) {
             el = iterator.next();
             sb.append("Contenuto: " + el.getText() + "\n");
        }
       
        return sb.toString();
    }
    catch(Exception e) {
        e.printStackTrace();
    }
       
    return null;
}

Questo metodo ti regala la stringa con i valori di tutti quei tag che hanno come classe className.
Ho spostato la creazione dell'Iterator fuori dal ciclo perchè all'interno non mi piace poi più di tanto. Non so se influisca o meno sulle performance, ma mi pare più chiaro lasciarlo fuori da istruzioni for() e while(). Forse sarebbe pure meglio usare un Array invece di una List, mmm...

EDIT: ovviamente, mi ero dimenticato il più grande problema di tutti: la creazione di un nuovo oggetto ad ogni invocazione di ciclo. Ora dovrebbe essere sistemato.
« Ultima modifica: 09 Aprile 2010, 00:12:08 CEST da .zero »
"Programmare" is not equal to "scrivere codice" || The sky is the limit... on an ideal, not-existent calculator.

Offline Dado

  • Nuovo arrivato
  • *
  • Post: 33
  • Respect: +7
    • Mostra profilo
  • Dispositivo Android:
    Motorola Milestone
  • Play Store ID:
    Edges Labs
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #7 il: 09 Aprile 2010, 10:56:35 CEST »
0
Premetto che sono nuovo  a java e a android. Ho provato ad importare htmlparser in eclipse ma quando lo importo ricevo un errore. eclipse mi chiede di reimpostare il percorso delle classi (path builder). Come si deve organizzare il percorso delle classi per non avere questo errore

Il problema te l'ha dato usando la funzione import di eclipse?
Puoi verificare i path cliccando con il destro sul progetto -> Build Path -> Configure Build Path e di lì puoi controllare che tutti i path/inclusioni siano corretti.
In alternativa visto che cmq si tratta di pochi file prova a creare un progetto nuovo tanto si tratta di importare una libreria (htmlcleaner) e i 2 file del progetto

Offline tava

  • Nuovo arrivato
  • *
  • Post: 8
  • Respect: +2
    • Mostra profilo
  • Sistema operativo:
    Mac Os X 10.6
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #8 il: 22 Aprile 2010, 08:49:07 CEST »
0
Ciao a tutti,
sono nuovo del mondo android e ho iniziato ieri sera a programmare.

Guardando questo ottimo tutorial ho iniziato a fare parsing di varie pagine web e devo dire che si riescono a fare delle belle cose in modo semplice e facile.

Ieri sera in un paio d'ore ho fatto vari tentativi su pagine molto diverse. Purtroppo la soluzione che ha fornito Dado (ottima comunque) ha un piccolo difetto quando si vogliono leggere dati da pagine html con codifica non unicode.
Faccio un piccolo esempio: la pagina della gazzetta dello sport è in ISO-8859-1 mentre Java lavora in Unicode quindi tutti i caratteri speciali accentati vengono sbagliati.
Ho risolto utilizzando il codice che inserisco qui sotto in modo che possa essere d'aiuto a tutti quelli che lamentano il mio stesso problema (e a cui vorrei non far perdere 2 ore in tentativi vari come successo a me):

Codice (Java): [Seleziona]
cleaner.clean(new InputStreamReader(conn.getInputStream(), Charset.forName("ISO-8859-1")));

Offline JD

  • Amministratore
  • Utente storico
  • *****
  • Post: 1600
  • Respect: +232
    • leinardi
    • Mostra profilo
  • Dispositivo Android:
    LG Nexus 5
  • Sistema operativo:
    L'ultima Ubuntu
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #9 il: 22 Aprile 2010, 12:11:11 CEST »
0
Ciao tava, grazie per condiviso questa utile informazione :)
È stata trovata una soluzione al tuo problema?
Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato ;).
E se hai aperto tu il thread marcalo come risolto cliccando !

Offline onire

  • Utente junior
  • **
  • Post: 52
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Dream G1, Samsung Galaxy S
  • Sistema operativo:
    Windows Xp
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #10 il: 26 Aprile 2010, 14:26:06 CEST »
0
Salve, complimenti per l'ottimo tutorial come sempre, alla luce di quanto visto vorrei sottoporvi nuovamente un mio problema, magari può essere lo spunto per un nuovo tutorial, ovvero come fare a gestire le connessioni ai siti che richiedono login e password.
Ogni suggerimento è gradito.
 

Offline ilcaduceo

  • Nuovo arrivato
  • *
  • Post: 12
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Acer Liquid
  • Sistema operativo:
    Ubuntu 10.04
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #11 il: 04 Maggio 2010, 22:51:38 CEST »
0
Ciao ragazzi, il tutorial ha aiutato parecchi di noi e continua ancora a dare i suoi frutti, complimenti all'autore  ;-)

Tuttavia ho un problema, sto letteralmente impazzendo! In pratica nella mia applicazione, precisamente quando viene eseguita l'istruzione:
 
Codice (Java): [Seleziona]
//url è una stringa con un url valido
TagNode root = cleaner.clean(new URL(url));
su un device android 2.1 viene eseguita senza problemi, mentre su device con android 1.6 lancia una IOException  o_O
Provato sia su emulatore che su device fisici... Non capisco proprio dove possa essere il problema!

AGGIORNAMENTO: durante la fase di debug ho notato che in altri metodi in cui è presente praticamente la stessa istruzione, questa viene eseguita senza problemi!

ULTERIORE AGGIORNAMENTO: ancora durante la fase di debug, ho suddiviso l'istruzione per cercare di capire quale metodo lancia l'eccezione, in questo modo:
Codice (Java): [Seleziona]
URL urlOb = new URL(url);
InputStream stream = urlOb.openStream();
TagNode root = cleaner.clean(stream);
e così ho scoperto che il problema avviene nel metodo "openStream", la cosa strana è che in pratica su eclair funziona perfettamente, mentre su donut, il problema si verifica su un url in particolare  o_O

Non è che è un bug, o qualcosa del genere che su eclair è stato risolto? Oppure?

ULTIMO AGGIORNAMENTO: sembra che non sia un problema di android o di codice java errato, bensì del sito da cui reperisco le informazioni e dei record dns associati... Ora sta da capire come risolvere il tutto  :-\

Chi può aiutarmi?
« Ultima modifica: 06 Maggio 2010, 01:26:46 CEST da ilcaduceo »

Offline onire

  • Utente junior
  • **
  • Post: 52
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Dream G1, Samsung Galaxy S
  • Sistema operativo:
    Windows Xp
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #12 il: 13 Maggio 2010, 22:24:52 CEST »
0
Domanda
Codice: [Seleziona]
try {   
        List<TagNode> elementi = rootnode.getElementListByAttValue("class", className);
va corretto così ?
Codice: [Seleziona]
List<TagNode> elementi = rootNode.getElementListByAttValue("class", className, true, true);

Offline riddick86

  • Nuovo arrivato
  • *
  • Post: 19
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    HTC Magic by Google
  • Sistema operativo:
    Mac OS X Snow Leopard
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #13 il: 24 Maggio 2010, 21:59:26 CEST »
0
Ho provato a scaricare la pagina di Lidl.it che è in ISO-8859-1.
Ho settato il Charset relativo, mi ha corretto le ì ma non la à esce "&agrave".
Qualche soluzione?

Offline Stefano Cipriani

  • Nuovo arrivato
  • *
  • Post: 8
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    htc wildfire
  • Sistema operativo:
    ubuntu
Re:[medio] Ottenere dati da una pagina HTML
« Risposta #14 il: 29 Marzo 2011, 16:28:39 CEST »
0
Ciao a tutti,
ho letto questo tutorial e ho cercato di utilizzarlo per una mia applicazione.
Essa dovrebbe visualizzare gli accordi di una canzone in una TextView leggendoli dal sito www.ultimate-guitar.com

L' app è formata da due classi di cui riporto il codice:
Classe CercaTablatura:
Codice (Java): [Seleziona]
public class CercaTablatura extends Activity {
       
        //String url_str = "http://www.anddev.it/";
]       String s = null;
        //Provo ad aprire l'url degli accordi(e' una pagina di esempio)
        String url_str = "http://tabs.ultimate-guitar.com/a/alex_ubago/aunque_no_te_pueda_ver_crd.htm";
    @Override
    public void onCreate(Bundle savedInstanceState) {
       
        super.onCreate(savedInstanceState);
       
        //tab_layout e' un LinearLayout che contiene una TextView
        setContentView(R.layout.tab_layout);

        TextView tv = (TextView)findViewById(R.id.myTextView);
        try {
               
                HtmlParser hp = new HtmlParser(url_str);
                s = hp.trovaTab();
                Log.d("Lunghezza stringa",""+s.length());
     
               
                /*Stampo il corpo della tablatura
                if (s == null){
                        Log.d("Tab", "Non c'è testo");
                }
                else{
            /* Se il testo è != null lo stampo nel Log. */

                Log.d("Tab", "Tab: "+s);
                }
                tv.debug(0);
                tv.setBackgroundColor(Color.WHITE);
                tv.setTextColor(Color.BLACK);
                tv.setMovementMethod(new ScrollingMovementMethod());
                tv.setText(s);
               
             
        } catch (Exception e) {
             /* In caso di errore, passiamo l'errore alla TextView. */
             tv.setText("Problema nel parsing: \n" + e.getMessage());
             Log.e("Error", e.toString());
        }
       
    }

}

Questo invece e' il codice della seconda classe
Codice (Java): [Seleziona]
public class HtmlParser {
       
        // oggetto TagNode radice del file html
        private TagNode rootNode;
        private String url_str = null;
        public StringBuffer sb = new StringBuffer();
       
        public HtmlParser(String url_str)
        {
                //URL di cui si vuole fare il parsing
                this.url_str = url_str;
               
                // inizializzazione dell'oggetto HtmlCleaner utile a generare un html pulito
                HtmlCleaner cleaner = new HtmlCleaner();
                CleanerProperties props = cleaner.getProperties();
                props.setAllowHtmlInsideAttributes(true);
                props.setAllowMultiWordAttributes(true);
                props.setRecognizeUnicodeChars(true);
                props.setOmitComments(true);
                 

                // apertura della connessione
                URL url;
                try {
                       
                        url = new URL(url_str);
                        URLConnection conn = url.openConnection();
                        //ora utilizziamo l'oggetto cleaner per "ripulire" l'html e inizializzare l'oggetto rootNode
                        rootNode = cleaner.clean(new InputStreamReader(conn.getInputStream()));
                       
                       
                } catch (MalformedURLException e) {
                        // TODO Auto-generated catch block
                        Log.e("Error", e.getMessage());
                       
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        Log.e("Errore", e.getMessage());
                }
        }
       
        public String trovaTab(){
       
                try {
               
       
                //Ottengo una lista di tutti gli elementi della pagina
                List<Object> elementi = rootNode.getAllElementsList(true);
               
 
               
                int i=0;
                for (Iterator<Object> iterator = elementi.iterator(); iterator.hasNext();)
            {
 
                TagNode Element = (TagNode) iterator.next();
               
                //Gli accordi della canzone sono contenuti in <div id="cont">
                if(Element.findElementByAttValue("id","cont" , false, true) != null){
                        i++;
                        sb.append("Tag["+i+"] " +
                                Element.findElementByAttValue("id","cont" , false, true).getText()
                                +"\n");
                        }
                else
                        sb.append("Nessuna corrispondenza\n"); 
               
               
            }
                return sb.toString();
             
        } catch (Exception e) {
             /* In caso di errore, stampiamo un messaggio */
                e.printStackTrace();
             //System.err.println("Impossibile parsare il file");
        }
        return null;
        }

               
}

Il metodo cercaTab dovrebbe scorrere ogni elemento della pagina e cercare al suo interno il tag che abbia attributo id di valore "cont" e copiarne
il testo in sb!
Purtroppo invece questo tag da me cercato sembra non essere presente, infatti in sb viene quasi sempre messa la stringa "Nessuna corrispondenza".
Cosa ancora più strana, ho provato a fare un progetto Java (non android) che facesse la stessa cosa e funziona perfettamente. Possiblie che la stessa istruzione 
Codice (Java): [Seleziona]
List<Object> elementi = rootNode.getAllElementsList(true); dia risultati diversi nel progetto java e in quello android?
Mi sembra assurdo!