lunedì 16 gennaio 2012

[VB.NET] Web Image Search con WebBrowser

Descrizione :
Un mio esempio pratico abbastanza completo basato sul Controllo WebBrowser.

+ Articolo :

Questo Articolo nasce da alcune discussioni su Forum Tecnici a cui ho partecipato con vari suggerimenti.
Ho deciso di riunire questi suggerimenti in qualcosa che avesse anche un minimo di "scopo pratico".
Inoltre le modifiche e aggiunte rispetto al codice originario sono parecchie.

Lo scopo è costruire una Windows Form basata sul WebBrowser che permetta la ricerca e il salvataggio veloce di immagini online, basandosi sul motore di Google Images.
Le immagini vengono cercate in base ad una o più Keywords e su Form sono presenti anche tutti i Controlli che servono a gestire le Keywords in modo ordinato.
Le immagini desiderate non vengono semplicemente salvate come thumbnails ( le stesse ottenute da Google ), ma il codice risale al link originale di ogni file e se possibile lo scarica alla fonte.
La selezione avviene con un menu contestuale.
Completa il tutto il salvataggio e caricamento delle Keywords usate in un file di testo.

La struttura dell'unica Form necessaria "FormMain" è semplice.
Tutte le proprietà sono assegnate via codice perciò non occorre alcuna particolare impostazione a Design.
I Controlli necessari sono :
--> Button : btn_salva
--> TextBox : txt_keywords
--> Button : btn_ok
--> CheckedListBox : chl_keywords
--> WebBrowser : wbr_google

La figura seguente rende bene l'idea della struttura e del funzionamento :


--> Codice FormMain :
Public Class FormMain

    Private percorso As String = Application.StartupPath & "\"
    Private cartellaAppunti As String = percorso
    Private cartellaImmaginiSalvate As New IO.DirectoryInfo(percorso & "ImmaginiSalvate\")
    Private nomeFileAppunti As String = "APPUNTI.txt"
    Private SelectedImageUrl As String
    Private urlGoogleImages As String = "http://www.google.com/images?&q="
    Private keywords As New List(Of String)
    Private selectedKeywords As New List(Of String)
    'Menu Contestuale
    Private WithEvents MenuImages As New System.Windows.Forms.ContextMenuStrip
    Private WithEvents ScaricaToolStripMenuItem As New System.Windows.Forms.ToolStripMenuItem

    Private Sub ListaKeywords()

        keywords.Sort()
        selectedKeywords.Sort()
        chl_keywords.Items.Clear()
        chl_keywords.Items.AddRange(keywords.ToArray)
        For Each kw As String In selectedKeywords
            If chl_keywords.Items.IndexOf(kw) > -1 Then chl_keywords.SetItemChecked(chl_keywords.Items.IndexOf(kw), True)
        Next

    End Sub

    Private Sub FormMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'Impostazioni Controlli
        With ScaricaToolStripMenuItem
            .Name = "ScaricaToolStripMenuItem"
            .Text = "Scarica"
        End With
        With MenuImages
            .Items.AddRange(New System.Windows.Forms.ToolStripItem() {ScaricaToolStripMenuItem})
            .Name = "MenuImages"
        End With

        With chl_keywords
            .Anchor = AnchorStyles.Top Or AnchorStyles.Bottom Or AnchorStyles.Left
            .CheckOnClick = True
        End With

        With wbr_google
            .Anchor = AnchorStyles.Top Or AnchorStyles.Bottom Or AnchorStyles.Left Or AnchorStyles.Right
            .IsWebBrowserContextMenuEnabled = False
        End With

        btn_ok.Anchor = AnchorStyles.Top Or AnchorStyles.Right
        txt_keywords.Anchor = AnchorStyles.Top Or AnchorStyles.Right Or AnchorStyles.Left

        'File Appunti / Keywords
        If IO.File.Exists(cartellaAppunti & nomeFileAppunti) = False Then IO.File.Create(cartellaAppunti & nomeFileAppunti)
        keywords = IO.File.ReadAllLines(cartellaAppunti & nomeFileAppunti).ToList
        ListaKeywords()

    End Sub

    Private Sub btn_salva_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_salva.Click

        IO.File.WriteAllLines(cartellaAppunti & nomeFileAppunti, keywords)

    End Sub

    Private Sub btn_ok_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_ok.Click

        If txt_keywords.Text = "" Then
            MessageBox.Show("Immettere almeno una keyword di ricerca !")
            Exit Sub
        End If

        Dim txt As String = txt_keywords.Text
        selectedKeywords = txt.Split(" ").ToList
        'Controllo correttezza keywords
        For i As Integer = selectedKeywords.Count - 1 To 0 Step -1
            selectedKeywords(i) = selectedKeywords(i).Trim
            If selectedKeywords(i) = "" Then selectedKeywords.RemoveAt(i)
            'Qui altri eventuali controlli sulle keywords...
            '...
        Next

        'Confronto con le keywords già in lista su chl_keywords
        For Each kw As String In selectedKeywords
            If Not keywords.Contains(kw) Then keywords.Add(kw)
        Next
        ListaKeywords()

        txt = String.Join("+", selectedKeywords.ToArray)
        wbr_google.Navigate(urlGoogleImages & txt)

    End Sub

    Private Sub chl_keywords_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chl_keywords.SelectedIndexChanged

        With chl_keywords
            If .CheckedItems.Contains(.SelectedItem) Then
                If Not selectedKeywords.Contains(.SelectedItem) Then selectedKeywords.Add(.SelectedItem)
            Else
                selectedKeywords.Remove(.SelectedItem)
            End If
        End With
        txt_keywords.Text = String.Join(" ", selectedKeywords.ToArray)

    End Sub

    Private Sub GoogleHtmlElement_MouseUp(ByVal sender As Object, ByVal e As HtmlElementEventArgs)

        If e.MouseButtonsPressed = Windows.Forms.MouseButtons.Right Then
            Dim HE As HtmlElement = DirectCast(sender, HtmlElement).Parent
            Dim LinkMatch As System.Text.RegularExpressions.Match = System.Text.RegularExpressions.Regex.Match(HE.OuterHtml, "imgurl=(?<imgurl>.*?)&amp", _
                                                                                                               System.Text.RegularExpressions.RegexOptions.IgnoreCase)
            If LinkMatch.Success Then
                SelectedImageUrl = LinkMatch.Groups("imgurl").Value
                MenuImages.Show(MousePosition)
            End If
        End If

    End Sub

    Private Sub ScaricaToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ScaricaToolStripMenuItem.Click

        Dim fileImmagine As System.IO.FileInfo
        Dim nomeImmagine As String
        Dim soggettoImmagine As String
        Dim soggettoRicerca As String = String.Join("+", selectedKeywords.ToArray)
        Dim contaImmaginiPerSoggetto As Integer

        For Each fileImmagine In cartellaImmaginiSalvate.GetFiles
            nomeImmagine = fileImmagine.Name
            soggettoImmagine = nomeImmagine.Split("_")(0)
            If soggettoImmagine = soggettoRicerca Then contaImmaginiPerSoggetto += 1
        Next

        Dim numero As String = (contaImmaginiPerSoggetto + 1).ToString("00000")
        '00000 --> nomi da 00001 a 99999
        Dim nomeFile As String = cartellaImmaginiSalvate.FullName & soggettoRicerca & "_" & numero & ".jpg"

        Dim immagineDaScaricare As New Net.WebClient
        Try
            immagineDaScaricare.DownloadFile(SelectedImageUrl, nomeFile)
            MessageBox.Show("Ok: Immagine Salvata " & nomeFile)
        Catch ex As Exception
            MessageBox.Show("Errore : Impossibile Salvare l'Immagine", "masterdrive", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        Finally
            immagineDaScaricare.Dispose()
        End Try

    End Sub

    Private Sub wbr_google_DocumentCompleted(ByVal sender As System.Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) Handles wbr_google.DocumentCompleted

        Dim readyState As WebBrowserReadyState = DirectCast(sender, WebBrowser).ReadyState
        If readyState = WebBrowserReadyState.Complete Then
            If e.Url.Host = "www.google.it" Then
                For Each img As HtmlElement In wbr_google.Document.Images
                    AddHandler img.MouseUp, AddressOf GoogleHtmlElement_MouseUp
                Next
            Else
                For Each img As HtmlElement In wbr_google.Document.Images
                    AddHandler img.MouseUp, AddressOf GoogleHtmlElement_MouseUp
                Next
            End If
        End If

    End Sub

End Class

La scelta delle Keywords viene fatta tipicamente dalla CheckedListBox, ma è anche possibile scrivere direttamente nella TextBox centrale.
Il programma verificherà l'inserimento di nuove Keywords e se caso le metterà in lista.
Pensa il codice, di volta in volta, a creare la giusta stringa da dare in pasto al browser nel caso di più Keywords :


Ho volutamente scritto alcuni spazi dei nomi per esteso per rendere più chiaro il percorso delle Classi .NET usate.
Inoltre parecchie impostazioni sono effettuate via codice e lo stesso menu contestuale è completamente creato e gestito via codice.
In uno scenario pratico, con i dovuti Imports ecc. il codice sarebbe più snello.

Ci sono inoltre vari spunti interessanti su cui focalizzare l'attenzione :
- Creazione e gestione di menu contestuale da codice ( no Design ).
- L'Evento DocumentCompleted() del WebBrowser e relativo uso del WebBrowserReadyState.
- L'estrazione dei link desiderati dal Document, grazie anche alle Regular Expression, e successivo download con Net.WebClient.
- Un valido sistema per comporre dinamicamente e progressivamente i nomi dei file salvati, ecc...

+ Fine Articolo.


Un Click su "Mi Piace" è il modo migliore per ringraziare l'autore di questo articolo.



5 commenti:

Anonimo ha detto...

Fine article. I used this in my own app. :)
Maybe it could search other sites, like yahoo, bing, and so on...
I'll keep an eye on this blog. Bye. :)

MarcoGG ha detto...

In fact. My very first intention was to give support for more search engines, but that would have involved different html code handling for each one, 'cause every search engine gives its own results differently. By the way, EVERY ARTICLE on my Blog has not to be considered as "FINISHED" or "CLOSED" forever. Articles can grow, change, and get updates in time. Stay Tuned... ;-)

Anonimo ha detto...

Funziona tutto, ma non riesco a scaricare l'immagine.. cliccando col tasto destro non compare l'opzione "Scarica".

MarcoGG ha detto...

Ciao, il codice è corretto. Non rilevo problemi. Vedi mia risposta su FB...

Riccardo ha detto...

Sisi grazie, ho letto :)

Posta un commento

 
Design by Free WordPress Themes Modificato da MarcoGG