Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
Guida al Visual Basic .NET - Parsing di codice HTML

Guida al Visual Basic .NET

Capitolo 80° - Parsing di codice HTML

<< Precedente Prossimo >>

È possibile scaricare pagine web in molti modi diversi, di cui WebBrowser è solo il primo che ho introdotto. Nel capitolo precedente non ci siamo posti alcun problema sull'analsi del codice di una pagina, poiché l'importante era riuscire a visualizzarla ed a navigare da essa ad alre pagine presenti in rete. Tuttavia, presto o tardi incorrerete nel bisogno di ottenere informazioni sui tag html presenti in un data pagina, ad esempio per effettuare un login automatico senza essere personalmente al computer, o per aggiungere alcune funzionalità al browser che state scrivendo di nascosto.
Per nostra fortuna esistono un paio di classi, HtmlDocument e HtmlElement, che eseguono autonomamente il parsing del sorgente html e ci permettono di agire su di esso mediante oggetti di alto livello.

Uno sguardo alle classi

HtmlDocument è la classe di partenza, che ci permette di iniziare ad ispezionare il codice. Essa non espone costruttori, né metodi statici, e quindi non esiste alcun modo di inizializzarla o di applicarla ad un file html. L'unico modo in cui possiamo ottenerne un'istanza è attraverso la proprietà Document del controllo WebBrowser. HtmlDocument espone alcuni membri interessanti:

  • ActiveElement : restituisce un oggetto HtmlElement che rappresenta l'elemento che possiede il focus al momento. Può indicare, ad esempio, un tag textarea se l'utente sta digitando del testo, od un div se è stato selezionata una parte di paragrafo;
  • All : restituisce una collezione di tutti i tag presenti nel documento, sempre sottoforma di HtmlElement;
  • Body : restituisce l'elemento body della pagina;
  • CreateElement(tagName) : crea un nuovo HtmlElement con tagName dato. Questo è l'unico modo in cui possiamo creare nuovi oggetti da aggiungere alla pagina (tranne ovviamente ricopiare il codice, modificarlo, e poi impostare di nuovo la proprietà DocumentText);
  • Forms : restituisce una collezione di tutti i tag form presenti nel documento;
  • GetElementById(id As String) : restituisce un riferimento all'elemento con specifico id;
  • GetElementFromPoint(p As Point) : restituisce un riferimento all'elemento che contiene il punto p; le coordinate del punto sono relative all'estremo superiore sinistro della pagina;
  • GetElementsByTagName(tagName As String) : restituisce una collezione di tutti i tag con dato tagName. GetElementsByTagName("div"), ad esempio, restituisce l'insieme di tutti i div della pagina;
  • Images : restituisce una collezione di tutti i tag image;
  • InvokeScript(scriptName As String, args() As Object) : esegue il metodo di nome scriptName passandogli gli argomenti specificati in args. Il metodo deve essere definito all'interno di un tag script nella pagina (non è importante il linguaggio, ma per ora ho verificato che funzioni solo con javascript e actionscript);
  • Links : restituisce una collezione di tutti i tag a;
  • Title : indica il titolo della pagina;
  • Url : l'indirizzo della pagina caricata;
  • Window : restituisce un oggetto HtmlWindow associato alla finestra che visualizza la pagina. Questo oggetto espone alcuni membri molto interessanti, tra cui:
    • Alert(S) : visualizza il messaggio S in una finestra di dialogo;
    • Confirm(S) : visualizza il messaggio S in una finestra di dialogo e permette di scegliere tra OK e Annulla; restituisce True se è stato premuto OK, altrimenti False;
    • Prompt(S, D) : visualizza il messaggio S in una finestra di dialogo e chiede di inserire un valore in una casella di testo (il valore predefinito è D). Restituisce il valore che l'utente ha immesso.

Gli oggetti HtmlElement contengono più o meno gli stessi membri, con l'aggiunta di GetAttribute e SetAttribute per modificare gli attributi di un tag.
Nei prossimi paragrafi farò alcuni esempi di come utilizzare tali classi.

Login automatico

Ecco un modo con cui potreste automatizzare il login in una pagina salvando le informazioni e compilando i campi con un solo pulsante.
Ipotizziamo di avere un dizionario LoginInfo in cui sono contenute delle coppie indirizzo-dizionario. I valori sono a loro volta altri dizionari che contengono le informazioni per il login. Potremmo utilizzare il codice seguente per automatizzare il tutto:

Class Form1

    'Qui c'è il codice del capitolo precedente
    
    'Ecco il dizionario che contiene tutto
    Dim LoginInfo As New Dictionary(Of String, Dictionary(Of String, String))

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Per semplicità, in questo esempio carichiamo dei
        'dati di prova al caricamento del form
        
        Dim TInfo As New Dictionary(Of String, String)
        With TInfo
            'ID del form di login
            .Add("form-id", "totemlogin")
            'ID della textbox per l'username
            .Add("username-field", "lname")
            'ID della textbox per la password
            .Add("password-field", "lpassw")
            'Username e password
            .Add("username", "prova")
            .Add("password", "prova")
        End With

        'Associa alla pagina il suo login
        LoginInfo.Add("http://totem.altervista.org/guida/versione3/login.php", TInfo)
    End Sub


    Private Sub btnAction_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAction.Click
        'Quando viene premuto il pulsante, ricava il dizionario
        'dei dati dall'url della pagina
        Dim Info As Dictionary(Of String, String) = LoginInfo(wbBrowser.Url.ToString())
        'Quindi compila i campi e invia la richiesta di login
        With wbBrowser.Document
            .GetElementById(Info("username-field")).SetAttribute("value", Info("username"))
            .GetElementById(Info("password-field")).SetAttribute("value", Info("password"))
            'InvokeMember invoca un metodo usabile da un
            'certo elemento. I metodi sono gli stessi che si
            'usano in javascript
            .GetElementById(Info("form-id")).InvokeMember("submit")
        End With
    End Sub

End Class

Nonostante possa semprare inutile, questo approccio potrebbe diventare molto più intrigante, ad esempio, se l'utente immettesse semplicemente una tessera o una chiavetta in un dispositivo collegato al computer e, usando il vostro browser, potesse interfacciarsi con tale dispositivo per automatizzare e personalizzare tutti i login a seconda dell'utente. Oppure potreste sfruttare il riconoscimento vocale offerto dalle librerie del framework 3.5 per confermare l'accesso mediante una parola detta a voce.

Trasformazioni

Nel paragrafo precedente ho mostrato come modificare degli elementi. In questo mostrerò come aggiungere nuovi elementi alla pagina dinamicamente e come gestirne gli eventi.
Sempre tenendo come guida il codice proposto nel paragrafo precedente, cambiamo la funzione del pulsante btnAction con la seguente: dopo il click sul pulsante, cliccando su qualsiasi immagine nella pagina, questa viene trasformata in un link all'immagine. Ecco il codice:

Class Form1

    '...
    
    Private Sub btnAction_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAction.Click
        With wbBrowser.Document
            'Scorre tutte le immagini nella pagina e ad ognuna
            'aggiunge un nuovo gestore d'evento per l'evento OnClick.
            'Il metodo AttachEventHandler può essere usato
            'da qualsiasi HtmlElement, ed accetta come primo
            'parametro il nome dell'evento da gestire (vedere la
            'documentazione ufficiale W3C) e come secondo un
            'delegate che punta al sottoscrittore.
            For Each img As HtmlElement In .Images
                .AttachEventHandler("onclick", AddressOf ImageToLink)
            Next
        End With
    End Sub
    
    'Questo è il nuovo gestore d'evento. Nonostante i
    'parametri, sender è sempre Nothing
    Private Sub ImageToLink(ByVal sender As Object, ByVal e As EventArgs)
        'Ottiene un riferimento all'immagine con il metodo
        'GetElementFromPoint, sfruttando il fatto che questo
        'codice viene eseguito subito dopo un click.
        'MousePosition indica la posizione del mouse sullo schermo,
        'Me.Location determina la posizione del form sullo schermo
        'e wbBrowser.Location la posizione del browser sul form.
        'La differenza tra questi punti è la posizione
        'del mouse rispetto al browser. Anche se un po' grezzo,
        'questo metodo dovrebbe funzionare abbastanza
        Dim Img As HtmlElement = wbBrowser.Document.GetElementFromPoint(MousePosition - Me.Location - wbBrowser.Location)
        'Crea un nuovo link mediante il metodo CreateElement
        'di HtmlDocument
        Dim Link As HtmlElement = wbBrowser.Document.CreateElement("a")

        'Imposta l'attributo href dell'immagine
        Link.SetAttribute("href", Img.GetAttribute("src"))
        'Imposta il testo del link
        If Not String.IsNullOrEmpty(Img.GetAttribute("longdesc")) Then
            Link.InnerText = Img.GetAttribute("longdesc")
        ElseIf Not String.IsNullOrEmpty(Img.GetAttribute("alt")) Then
            Link.InnerText = Img.GetAttribute("alt")
        Else
            Link.InnerText = "Immagine"
        End If

        'Aggiunge il link prima dell'immagine
        Img.INSERT IGNOREAdjacentElement(HtmlElementINSERT IGNOREionOrientation.BeforeBegin, Link)
        'Dato che non è possibile eliminare elementi,
        'impone all'immagine larghezza 0
        Img.SetAttribute("width", "0")
    End Sub

End Class
<< Precedente Prossimo >>
A proposito dell'autore

C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...