Canalblog
Editer l'article Suivre ce blog Administration + Créer mon blog
Publicité
Fanfois et ses papillons
3 octobre 2011

Aspirateur (1 - cahier des charges)

Dans cette nouvelle série de billets nous allons réaliser un aspirateur de sites webs.

Pourquoi réaliser nous même un aspirateur alors que l'on peut trouver assez facilement, et gratuitement, ce type de programme sur la toile ?

Principalement pour deux raisons :

  1. les aspirateurs tout fait ne permettent pas toujours de faire ce que l'on souhaite faire ;
  2. en tant que développeur, l'exercice permet d'aborder plein de sujets utiles pour d'autres choses et donc d'apprendre.

Ma raison personnelle est la suivante.

Je suis abonné depuis des années à un site web payant qui propose un contenu pédagogique que je trouve très bien. Tellement bien que j'aimerais pouvoir utiliser ce contenu même lorsque je n'ai pas de connexion internet (moi et la 3G ça fait deux). Au début, il y a dix ans, il n'y avait pas d'autre solution que d'aspirer tout le site. Maintenant le site permet le téléchargement des vidéos pédagogiques. Il y a du progrès, on peut visualiser les vidéos à tout moment pour peu qu'on les ait téléchargées chez soi. Cependant il faut toujours être connecté pour lire les textes et voir les images. C'est donc mieux mais pas parfait. Il faut par exemple retrouver la bonne vidéo sur son disque et la lancer manuellement. On peut certes écrire un programme qui le fasse, mais dans ce cas aspirer le site conduit à une expérience utilisateur nettement meilleure (on croirait une réponse à appel d'offres).

Quels problèmes allons-nous devoir gérer ?

  • Qui dit site payant dit généralement authentification ;
  • Les sites sont généralement (toujours) dynamiques. Le résultat de notre aspiration ne le sera pas. Comment régler ce problème ?
  • Qui dit site internet dit bien souvent publicité. Il n'est pas dans notre intention de récupérer cette dernière.
  • Tout le site ne nous intéresse peut-être pas, Comment isoler la bonne partie ?
  • Pour éviter que l'aspiration ne s'éternise, il serait bon de ne pas récupérer les fichiers statiques plus d'une fois.

 

Le premier problème, celui qui empêche souvent d'utiliser un aspirateur trouvé sur le web, c'est l'authentification.
C'est un peu fait pour, même si empêcher l'aspiration n'est pas la raison initiale de l'authentification, et il est normal que cela pose un problème. Dans les billets à venir nous verrons comment faire pour comprendre le fonctionnement de l'authentification et donc permettre à notre aspirateur de réaliser cette tâche. Evidemment pour s'authentifier il faudra avoir un compte valide sur le site ciblé, le programme ne vous permettra pas de récupérer des données auxquelles vous n'avez pas accès.

Le second problème n'en est pas un si l'on dispose d'un espace disque suffisant et/ou si on limite les pages aspirées. Il est certain que nous ne souhaitons pas reconstruire un site dynamique. Premièrement parce que le PC sur lequel nous déposerons le résultat de notre récupération n'est pas un serveur web. D'autre part tout le monde n'a pas les connaissances suffisantes pour monter un site web dynamique sur son PC et ce que nous aurions construit pourrait être inutilisable. Mais la meilleure raison est que si générer un site dynamique parfaitement fonctionnel était aussi simple, ça se saurait.
Notre résultat sera donc un ensemble de fichiers statiques permettant visuellement d'avoir quelque chose de (très) proche du site initial. L'utilisateur cliquera sur un fichier HTML et c'est tout.
De ce fait nous pourrons être amenés à récupérer des pages web, éventuellement grosses, qui sont en fait construites dynamiquement par le site initial. Typiquement un index ou une table des matières.
Cela nous obligera également, si nous souhaitons pouvoir naviguer correctement dans notre copie du site, à récupérer l'intégralité du site lors d'une mise à jour. En effet si nous ne faisons pas cela, les anciennes pages (celles récupérées avant) ne référenceront pas les nouvelles et la navigation dans la copie sera défaillante.

Le troisième point est normalement simple à réaliser car les publicités sont généralement gérées par des sites spécialisés. Une simple analyse basique des URI permet généralement d'éliminer de la récupération ce type de contenu.

Les quatrième et cinquième points reposent sur la mémorisation de l'arborescence du site aspiré.
Souvent un site est découpé en zones fonctionnelles, découpage que l'on retrouve dans les URL des pages. Eliminer une partie du site revient donc à ne pas aspirer les pages ayant une URL commençant par un préfixe donné.
Comme nous l'avons dit, le fait d'aspirer un site nous amène généralement à aspirer tout le site à un instant donné pour avoir une navigation interne au site optimal (en tout cas équivalente à celle d'origine). Le problème c'est qu'aspirer l'intégralité d'un site ayant plusieurs années d'existence prend un temps assez important, voir trop important même avec une bande passante élevée. Heureusement seuls les liens dans les pages changent, les ressources utilisées par les pages ne changent pas. Il y a certes de nouvelles ressources (images, vidéos, pdf …) mais les anciennes ressources (celles que nous avons aspirées la fois précédente) n'ont pas subies de modifications. Si elles ont été modifiées, et que les personnes qui gèrent le site ont un peu de plomb dans la tête, leur nom a également changé et donc elles seront considérées comme nouvelles pour nous. Le risque est seulement que nous conservions des fichiers qui ne sont plus utilisés par notre copie du site. Automatiser un nettoyage n'est pas une tâche insurmontable même si nous ne le ferons pas forcement dès le départ.
Pour faire simple nous pouvons statuer que tout fichier de ressource déjà présent sur notre disque dur nous amènera à considérer la ressource comme venant d'être récupérée.

Maintenant que nous avons une petite idée de ce que nous allons faire il faut décider comment nous allons le faire. Sur ce blog c'est .NET et plutôt C#. Pour être dans l'air du temps WPF, même si le gros du problème n'est pas l'IHM. Pour faire presque pro il serait bon de pouvoir tester notre code donc de faire une librairie de services.
Comme nous allons utiliser Microsoft Visual C# 2010 Express, nous n'aurons pas les tests directement dans notre projet. Pour cela il nous faudrait utiliser une version payante de VS et même s'il est fort probable que vous et moi en disposions d'une, ce n'est pas l'idée. Les tests ne seront donc pas présentés (au moins au début) ici mais le découpage rendra leur mise en œuvre assez simple.

Création du squelette du projet

Lançons Microsoft Visual C# 2010 Express et créons un "Nouveau projet…" de type "Application WPF" dénommé Aspirateur. Nous ajoutons ensuite un second projet de type "Bibliothèque de classes" dénommé AspirateurUtil. Il aurait été intéressant d'ajouter le projet de test mais sauf à avoir une version payante de VS ce n'est pas possible. Pour être tranquille nous sauvons tout ce petit monde ce qui nous permet de préciser l'emplacement où nous voulons mettre nos fichiers.

Nous allons commencer par coder de quoi ouvrir la première page de notre site internet cible. Pour cela il nous faut définir dans le projet WPF, une zone de saisie pour donner l'URL cible, un bouton pour lancer la récupération de la page et une zone texte pour afficher le contenu de la page récupérée.

<Window x:Class="Aspirateur.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Label Content="Site cible :" Height="28" HorizontalAlignment="Left" Margin="16,20,0,0" Name="lblTargetURL" VerticalAlignment="Top" />
        <TextBox Height="23" Margin="85,22,33,0" Name="txtTargetURL" VerticalAlignment="Top" />
        <Button Content="Aspire" Height="23" HorizontalAlignment="Left" Margin="16,71,0,0" Name="btnGo" VerticalAlignment="Top" Width="75" />
        <ScrollViewer Margin="16,112,33,23">
            <TextBlock Margin="0,0,0,0" Name="txtHTML" Text=""  />
        </ScrollViewer>
    </Grid>
</Window>

Nous double cliquons sur le bouton pour accéder au gestionnaire de son événement click et récupérons l'URL se trouvant dans la TextBox. Les plus courageux pourront la valider avec une expression régulière, les autres se contentent d'appeler une fonction de la librairie qui récupère ce qui se trouve derrière cette URL et le retourne sous la forme d'un texte :

private void btnGo_Click(object sender, RoutedEventArgs e)
{
    txtHTML.Text = DownloadHelper.GetPage(txtTargetURL.Text);
}

Pour que cela puisse fonctionner il faut référencer AspirateurUtil dans Aspirateur et ajouter le using AspirateurUtil; indispensable.

A ce stade, ni la classe DownloadHelper, ni la fonction GetPage n'existent, il faut les créer dans la librairie AspirateurUtil. On renomme donc la classe et le fichier créés par défaut en DownloadHelper. On met un code minimal de récupération sur le web pour tester. Cela donne :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
 
namespace AspirateurUtil
{
    public class DownloadHelper
    {
        public static string GetPage(string TargetURL)
        {
            // On récupère les données associées à l'URL.
            WebClient wc = new WebClient();
            string page = wc.DownloadString(TargetURL);
            return page;
        }
    }
}

Normalement nous devrions passer au projet de tests et ajouter les tests unitaires qui vont bien, sauf que…

Si nous lançons notre programme et que nous donnons l'URL complète et valide d'une page web, http://www.canalblog.com par exemple, nous constatons que nous récupérons bien quelque chose et que cela ressemble beaucoup à la page web attendu.

C'est normal car si le code ne contient pas l'ombre d'un début de gestion d'erreur, il est opérationnel.

Dans le prochain billet nous ajouterons de l'analyse du code source de la page récupérée pour pouvoir récupérer les pages liées à notre page de départ.

Publicité
Publicité
Commentaires
Fanfois et ses papillons
Publicité
Archives
Publicité