Création d'un aspirateur de sites Web
Je vous propose de créer un aspirateur de site web. On se contentera d'aspirer une page et d'aller chercher toutes les pages référencé sur celle ci. Le programme décrit ci dessous permet de télécharger tous les articles contenus dans la page de Rebolfrance (http://rebolfrance.multimania.com/articles.html). Ce petit exercice nous permettra de découvrir les joies de la lecture des pages web (read) et du parsing (parse) de chaînes de caractères.
Fonctions de lecture et d'écriture d'une URL
Pour faciliter la programmation, augmentons le dictionnaire de rebol avec quelque fonctions pour lire une page et l'écrire.
Voici une première fonction qui lit une url et renvoie son contenu :
lireUrl: function [ url ] [ page ] [ read ( to-url url ) ]
On voit ici que je n'ai pas spécifié la valeur de retour avec return. En effet rebol prend automatiquement la valeur de la dernière ligne de code. Ici la valeur retournée est l'évaluation du mot read. La fonction to-url permet de transformer la valeur entrée en paramètre, en type URL. Cela permet d'éviter de provoquer des erreurs d'utilisation de la fonction car ce sera toujours une URL et non un STRING qui sera reçu par read.
La deuxième fonction nécessaire écrit le contenu d'une variable dans un fichier :
ecrirePage: function [ nomPage page ] [ ] [ write ( to-file nomPage ) page ]
Utilisons ces deux fonction en téléchargeant la page d'accueil du site rebolfrance :
contenu: lirePage http://rebolfrance.multimania.fr
; le code html doit ensuite s'afficher à l'écran
print contenu; la page et enregistrée sur le disqueecrirePage 'index.html' contenu
Je vous propose maintenant de faire une fonction qui ajoute à une URL un chemin ou une page. Ceci permet de moins se fatiguer à chaque nouvelle page trouvée :-)
ajouterChemin: function [ url chemin ] [ urlChemin ] [url: to-url urlappend url "/"append url cheminreturn url]
exemple d'utilisation :
url: http://rebolfrance.multimania.frurlIndex: ajouterChemin url ''index.html''print urlIndex
résultat affiché: http://rebolfrance.multimania.fr/index.html
Remarque: On peut très bien faire la même chose avec des images ou des fichiers. Par contre, il faut utiliser le rafinement read/binary pour télécharger en mode binaire.
Parsing du contenu de la page
Une fois que l'index est chargé, il faut rechercher dans celui ci les balises <a href='' ''> pour connaître les liens présents dans cette page. Pour cela rebol dispose d'un mot idéal pour ce genre de chose : parse. Il permet en quelque ligne d'effectuer cette opération. Prenons un exemple pour expliquer le fonctionnement de parse :
a: ''je test le +mot+ parse ''
parse a [ thru ''+'' copy b to ''+'' ]
print b
résultat: mot
Parse permet de récupérer une partie de la chaîne parsée d'une valeur a vers une valeur b. Deux mots sont disponibles pour décrire la règle de parsing: to et thru. To permet de de définir que l'on parse à gauche de la valeur de recherche alors que thru permet de définir que l'on parse à droite de la valeur de recherche! Je m'explique. Reprenons l'exemple ci-dessus. Thru ''+'' veut dire que je recherche et enregistre la partie du texte qui est aprés ''+'' (à droite de ''+''). To ''+'' veut dire que je recherche et enregistre la partie du texte qui est avant ''+'' (à gauche de ''+''). Ici on obtient bien le résultat ''mot''.Si on avait écrit to ''+'' copy b thru ''+'' on aurait obtenu ''+mot+''.
Remarque : Pour comprendre un peu mieux, on peut observer que to en anglais veut dire dans ce contexte depuis et jusqu'à. Alors que thru veut dire à travers. To ''+'' copy b thru veut donc dire: je copy b depuis ''+'' à travers ''+''.
Il est donc très facile de faire la même chose avec la balise <a href='' ''> dans le contenu de la page "articles" de Rebolfrance. Créons une fonction qui prend le contenu d'une page web ( String! ) et qui renvoie une liste de liens ( Block! ).
parsePage: function [ page ] [ listePage ] [parse page [any [thru {<a href="} copy href to {"}( append listePage href )]]return listePage]
Ici on parse donc après ( à travers ) ''<a href= '''' et jusqu'à le prochain ''. La règle de parsing est donc bien thru {<a href="} copy href to {"} . Any va nous permettre de traiter toutes les occurrences de <a href>. Quand une balise est trouvée on l'ajoute au bloc listePage.
Voyons le résultat de la fonction parsePage :
print parsePage lireUrl http://rebolfrance.multimania.com/articles.htmlRésultat:
index.html autresites.html propagande/propagande.html articles.html bibliographie.html societes.html
faq.html http://forums.multimania.fr/lire/rebol telechargement.html mailto:rebolfrance@multimania.com
articles.html#contributions articles.html#login articles/execparam.html articles/delphi/delphi.htm
articles/cgi1/cgi1.html articles/routageemails.html articles/services.html articles/mysqlcmd/mysqlcmd.html
articles/services.html articles/login1/login1.htm articles/login2/login2.htm articles/login3/login3.htm
articles/login4/login4.html articles/login5/login5.html
On dispose désormais de tout ce qu'il faut pour arriver au but de cet exercice. Il nous faut maintenant éliminer tous les liens qui ne font pas référence à des articles et enregistrer les pages qui en sont. Pour cela, plus besoin de faire une fonction particulière, le code suivant et donc ajouté à la fin de notre script. Voici le code correspondant :
url: http://rebolfrance.multimania.comarticles: parsePage lireUrl ajouterChemin url "articles.html"foreach article articles [if found? find article {articles/} [parse article [ thru {/} copy page thru ".htm" ]parse page [ thru {/} copy page thru ".htm" ]ecrirePage page lireUrl ajouterChemin url article]]
Tout d'abord, on analyse la page ''articles.html'' pour avoir toutes les références. Pour chacun des articles, on enlève le chemin pour avoir juste le nom de la page, puis on lit et on enregistre la page.
Après l'exécution du script, les pages html de tout les articles de Rebolfrance, doivent apparaître dans le dossier d'exécution du script. De cette façon vous pouvez désormais mettre à jour et récupérer les nouveaux articles juste en lançant le script régulièrement!
Voici un récapitulatifde tout le programme nécessaire à l'enregistrement desarticles de Rebolfrance:
#!/usr/bin/rebol -s
rebol [ ]
ajouterChemin: function [ url chemin ] [ urlChemin ] [
url: to-url urlappend url "/"append url cheminreturn url
]
lireUrl: function [ url ] [ page ] [ read ( to-url url ) ]
ecrirePage: function [ nomPage page ] [ ] [ write ( to-file nomPage ) page ]
lireImg: function [ urlImg ] [ img ] [ read/binary ( to-url urlImg ) ]
ecrireImg: function [ nomImg img ] [ ] [ write ( to-file nomImg ) img ]
parsePage: function [ page ] [ listePage ] [
href: copy ""listePage: copy []parse page [any [thru {<a href="} copy href to {"}(append listePage href)]]return listePage
]
parseImg: function [ page ] [ listeImg ] [
img: copy ""listeImg: copy []parse page [any [thru {<img src="} copy img to {"}( append listeImg img)]]return listeImg
]
url: http://rebolfrance.multimania.com
articles: parsePage lireUrl ajouterChemin url "articles.html"
foreach article articles [
if found? find article {articles/} [print articleparse article [ thru {/} copy page thru ".htm" ]parse page [ thru {/} copy page thru ".htm" ]ecrirePage page lireUrl ajouterChemin url article]
]
Conclusion
Je ne suis pas rentré trop dans les détails de l'écriture complète d'un aspirateur de site web mais, avec ces quelques éléments, de nombreuses applications peuvent êtres réalisées. On peut par exemple imaginer un script similaire pour mettre à jour automatiquement une page web qui est souvent consultée par les menbres du personnel sur un intranet, pour éviter de nombreux accès vers l'extérieur. Ou bien récupérer des informations et les reformater à notre convenance à l'aide d'un script cgi.
Pour plus d'informations sur les fonction parse et read, consultez le manuel de Core téléchargeable sur le site officiel de rebol.
Emmanuel Sterbac