Récupérer et parser du HTML

Ma problématique était de récupérer des images depuis un site web. Il me fallait donc charger le contenu du site, puis chercher à l’intérieur du code source, les adresses des images qui m’intéressaient. Voyons tout cela plus en détail.

Récupérer une page nécessitant une authentification

Au départ, j’avais besoin de travailler sur les images des albums Facebook. Un album privé était donc difficilement accessible sans connexion. La manière la plus simple que j’ai trouvée a été d’utiliser le contrôle Web Browser en WPF.

J’ai donc créé un WebBrowser dans mon xaml que j’ai appelé WebBro. La navigation peut se faire depuis le code behind en une ligne :

WebBro.Navigate(VotreUrl);

Ensuite lorsque je suis sur la bonne page, je commence par récupérer et stocker le code HTML de la page :

dynamic dom = WebBro.Document;
string htmlText = dom.documentElement.innerHTML;

Le mot-clef dynamic simplifie le code mais vous pouvez très bien ajouter une référence à MSHTML et caster le WebBro.Document en mshtml.HTMLDocumentClass.
Nous avons donc récupéré le HTML de la page et l’avons stocké dans la string htmlText. Il nous restera plus qu’à travailler dessus pour rechercher les éléments que l’on souhaite. Nous verrons cela dans la troisième partie de cet article.

Récupérer une page “classique”

Pour une application WP7, il me fallait récupérer des images depuis un site mais pour cela il fallait tout automatiser. Le WebBrowser n’était donc pas imaginable.
J’ai utilisé un WebClient pour requêter le site et récupérer directement le HTML de la page.

_wc = new WebClient();
_wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(_wc_DownloadStringCompleted);
_wc.DownloadStringAsync(new Uri(VotreURL + _localAppItem.Name,
                                                UriKind.Absolute));

Vu que c’est du Windows Phone, tout est asynchrone, il faut donc s’abonner à l’évènement DownloadStringCompleted. Lorsque cet évènement est levé, nous pouvonsrécupérer le contenu de notre page HTML dans notre méthode _wc_DownloadStringCompleted(object o, DownloadStringCompletedEventArgs args).
Le code HTML de la page se trouvant dans la variable args.Result.

Parser le HTML grâce aux Regex

Les Regex… Ces petites bêtes que l’on souhaite éviter et qui ne se laisse pas dompter facilement.
Les Regex ou expressions régulières sont un moyen de chercher des éléments textuels respectant une certaine forme, un certain pattern.
Dans mon cas, pour chercher des images dans du code HTML, il me fallait récupérer toutes les chaines commençant par “src= »” et finissant avec une extension particulière “.jpg”, “.jpeg”, “.png”,…

Pour plus de lisibilités, j’ai donc défini deux chaînes, une pour le début et une pour la fin de la séquence à trouver :

string start = "src=\"";
string end = "\\.(jpg|png)";

Sur la chaîne start, le ‘\’ permet d’échapper le guillemet.
Sur la chaîne end, les ‘\\’ permettent d’échapper un ‘\’ qui permet d’échapper le ‘.’. En effet, dans une regex, le point est un caractère particulier.

Nous pouvons maintenant créer notre Regex :

Regex r = new Regex(start + "(.*?)" + end);

Nous créons donc une String que nous passons au constructeur de notre Expression Régulière. On retrouve notre chaîne de début et notre chaîne de fin avec au milieu cet assemblage étrange : « (.*?) » :

  • les parenthèse signifient un groupement de caractères
  • Le point signifie n’importe quel caractère
  • l’étoile signifie une à plusieurs fois le caractère précédent
  • le point d’interrogation permet de répéter l’expression précédente

Une fois cette Regex créée, il ne nous reste plus qu’à l’utiliser sur notre code html :

MatchCollection matches = r.Matches(htmlText);

A chaque fois qu’une occurrence sera trouvée, elle sera ajoutée à la collection matches qu’il nous suffit de parcourir avec un foreach pour récupérer les liens des images.

foreach (Match matche in matches)
            {
                VotreMethodePourTraiter(matche.Groups[1].Value);
            }

Bonus : Télécharger les images

Idem, je vais montrer avec deux possibilités. Synchrone en WPF et asynchrone pour WP7.

De manière synchrone

private void GetPics()
        {
                int i = 0;
                foreach (string url in urlBrutImg)
                {
                    System.Drawing.Image myImg;
                    WebRequest req = WebRequest.Create(url);
                    WebResponse response = req.GetResponse();
                    myImg = System.Drawing.Image.FromStream(response.GetResponseStream());
                    myImg.Save("D:\\NomDuRepertoire\\" + i+".jpg");
                    i++;
                }
        }

Chaque fois qu’une de mes Regex étaient valide, j’ajoutais l’url à une liste. C’est cette liste, nommée urlBrutImg que je parcours dans le foreach.
Je fais une WebRequest sur l’url obtenue, je génère mon image avec le Stream que me retourne la WebRequest. Il ne me reste plus qu’à enregistrer cette image sur mon disque dur, et d’incrémenter i pour ne pas écraser la précédente !

De manière asynchrone

Ici, chaque fois que ma Regex matchait, j’ajoutai l’Uri obtenue dans une Queue, ce type de liste permet de retirer un élément lors de sa lecture. J’ai réutilisé le WebClient qui m’a permis de télécharger le code HTML de manière asynchrone pour télécharger les images. Il m’a juste fallu m’abonner à un nouvel évènement :

_wc.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompleted);

J’ai ensuite ajouté une méthode Dequeue() pour retirer mes éléments au fur et à mesure que je récupérais mes images.

private void Dequeue()
        {
            if (_queuedItems.Count > 0)
            {
                Uri uri = _queuedItems.Dequeue();
                _wc.OpenReadAsync(uri, uri);
            }
        }

Et le code de ma méthode OpenReadCompleted pour enregistrer l’image :

private void OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            try
            {
                if (e.Error == null && e.Cancelled == false)
                {
                    Uri uri = e.UserState as Uri;
                    BitmapImage image = new BitmapImage();

                    image.SetSource(e.Result);
                    //Traitement de l’image                  
                }
            }
            catch (Exception ee)
            {}
            Dequeue();
        }

Une fois que vous avez votre BitmapImage, à vous de voir si vous voulez l’enregistrer dans l’IsolatedStorage ou l’afficher.
On fini par un Dequeue, pour continuer à vider notre collection et télécharger les images manquantes.

Conclusion

Dans cet article nous aurons vu plusieurs manières de récupérer du code HTML puis comment obtenir certains éléments similaires grâce aux Regex.

Par JC VASSELON

3 réponses à “Récupérer et parser du HTML

  1. Super tutoriel, je suis justement en train de construire ma première application et ce blog m’est d’une grande aide.

  2. Bon tuto mais j’aimerai savoir, la vrai différence entre le syncrone et le asyncrone en terme de performance et de facilité de programmation ?

  3. Asynchrone : Je te réponds pas immédiatement mais tu peux faire autre chose pendant ce temps.

    ça mérite un poil de réflexion au niveau développement. Mais le pattern async / await facilite ça dès Windows 8 / WP8.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s