samedi 24 novembre 2012

SCRIPT 4 : URLS - PAGES ASPIREES - DUMPS - UTF-8

Voici venue l'heure de mettre à profit tous les tests effectués précédement pour créer un programme qui génère à partir d'un dossier contenant plusieurs fichier d'URLs un tableau à 5 colonnes avec :

1-Le numéro de l'URL
2-Le liens vers l'adresse URL
3-Le lien vers la page web aspirée
4-Le lien vers le texte extrait de la page web aspirée
5-Le lien vers le texte au format UTF-8 extrait de la page web aspirée

En plus de créer des liens, nous devons donc gérer la capture de pages web, l'extraction de contenu textuel, la reconnaissance et la convertion d'encodage. Tout un programme !!!

Pour mettre au point notre script, nous nous sommes appuyés sur le schéma présenté sur le site du cours à l'adresse suivante:


Pour chaque boucle nous n'avons fait que reprendre et adapter des portions de script vus précédemment.

Afin débugger plus facilement le programme, nous avons ajouté pas mal de commentaires dans le script et nous avons tirer profit au maximum de la commande echo pour suivre le déroulement du programme dans le terminal.

Vous pouvez télécharger notre nouveau script à l'adresse suivante :


Comme vous verrez, nous en avons aussi profité pour arranger un tout petit peu la présentation des tableaux.

Voici des caputres d'écran faites après un test sur quelques URLs seulement :

Tableaux pour 2 fichiers contenant 3 URLS

Vue du terminal pendant l'execution du script


Normalement, nous avons réussi à régler la plupart des problèmes. On constate juste un type d'erreur dans le terminal au moment de converstion d'encodage avec la commande iconv. On trouve par exemple :

iconv: (stdin):8:8: cannot convert

Pourtant, tous les fichiers présentés dans la dernière colonne du tableau semblent avoir été convertis en UTF-8 ?

Il faudra donc éclaircir ce problème un autre jour... ou une autre nuit ;°) !

Parmi les améliorations possibles, on pourra encore :

- améliorer l'organisation des fichiers produits en output
- améliorer la présentation des tableaux
- ajouter une colonne aux tableaux pour les messages d'erreur (type: CURL, Erreur 404, encodage non reconnu...)
- prendre en compte plus d'erreurs que l'erreur 404 

RECUPERER ET CONVERTIR L'ENCODAGE D'UN FICHIER

Nous avons vu que pour récupérer l'encodage d'un fichier dans une variable, on pouvait utiliser la commande file avec ses options -I et -d suivie de la commande cut et de ses options -d et -f.

En bash, il est également possible de convertir le format d'encodage d'un fichier à condition de préciser dans les options de la fonction iconv l'encodage d'entrée (-f) et l'encodage de sortie (-t).

Voici un exemple à partir d'un fichier texte encodé en ISO-8859-1. On veut convertir son encodage en UTF-8. On peut donc saisir la commande suivante dans le terminal :

iconv -f iso-8859-1 -t utf-8 < kamikaz.txt > kamikaz-UTF-8.txt

Rappel : "<" signifie qu'on donne en entrée le fichier kamikaz.txt et ">" qu'on veut obtenir en sortie le fichier kamikaz-UTF-8.txt

Il existe beaucoup de formats d'encodage différents et pour obtenir la liste de ceux disponibles depuis le terminal, on peut saisir la commande iconv -l. Les conversions ne sont possibles qu'entre des types d'encodage apparenant à cette liste.

Grâce à la fonction egrep, on va donc pouvoir tester si l'encodage d'un fichier appartient à la liste disponible depuis le terminal. 

Voici une proposition de script :

#!/bin/bash
echo "Donnez le nom et l'emplacement du fichier";
read fichier;
encodage=$(file -I $fichier | cut -d= -f2);

detection=$(iconv -l | egrep -i $encodage);

if [[ $detection != "" ]]
then
echo "L'encodage est a été reconnu";
else
echo "L'encodage ne fait pas parti de la liste";
fi



Commentaires

- La commande file -I $fichier | cut -d= -f2 permet de récupérer l'encodage du fichier d'entré. On enregistre cette valeur dans la variable encodage.

- La commande iconv -l | egrep -i $encodage permet de rechercher une occurence de la valeur de la variable encodage dans la liste des encodages disponibles. L'option -i permet de ne pas prendre en compte la case. Si cette commande renvoie une valeur, c'est que l'encodage du fichier appartient à la liste des encodages disponibles. On a donc enregistré le résultat de cette commande dans la variable detection afin de pouvoir mettre en place un test sur la valeur de cette variable.

On réutilisera ce type de script dans une des boucles de notre programme traitant nos fichiers d'URLS. Si l'encodage d'une page web aspirée est reconnu, à la place d'afficher le message "L'encodage est a été reconnu", on pourra lancer une convertion avec la commande iconv et les options -f et -t.

CONNAITRE OU VERIFIER L'ENCODAGE D'UN FICHIER

La commande file suivi d'un nom de fichier permet d'afficher son type. On peut y ajouter l'option -i (ou -I sur Mac) pour plus d'informations sur l'encodage.

Voici un exemple sur le fichier texte.txt, contenant uniquement du texte :

file -I fichier.txt;


et voici la réponse dans le terminal :



fichier.txt: text/plain; charset=utf-8


A l'aide de la fonction cut et des options -d (qui permet de définir un délimitateur) et -f (qui permet de définir la zone à selectionner en fonction de ce délimitateur), on va pouvoir extraire de l'information obtenue par la commande file l'encodage d'un fichier.

En reprenant l'exemple ci-dessus, on saisira la commande :

file -I fichier.txt | cut -d= -f2

(Attention, sur PC il faudra peut-être remplacer -I par -i et | par ¦ )

Cette commande indique qu'on choisi comme délimitateur le symbole "=" qui sépare ici notre chaîne de caractère en deux parties :

fichier.txt: text/plain; charset     utf-8
           (1)                        (2)

Avec l'option -f2, on ne garde que le contenu de la deuxième partie, c'est à dire utf-8.

On peut capturer la valeur rendue par cette commande dans une variable qu'on appellera encodage :

encodage=$(file -I fichier.txt | cut -d= -f2)

A partir de cette variable, on pourra imaginer un script qui affiche ou test l'encodage d'un fichier. Voici deux petits exemple en bash qui fonctionnent sur Mac :

1) Script pour afficher l'encodage d'un fichier:

#!/bin/bash
echo "Donnez le nom et l'emplacement d'un fichier";
read fichier;
encodage=$(file -I $fichier | cut -d= -f2);
echo "Ce fichier est encodé en $encodage.";

2) Script pour déterminer si un fichier est encodé en UTF-8:

#!/bin/bash
echo "Donnez le nom et l'emplacement du fichier à tester";
read fichier;
encodage=$(file -I $fichier | cut -d= -f2);
if [[ $encodage == "utf-8" ]]
then
echo "Ce fichier est bien encodé en UTF-8.";
else
echo "Ce fichier n'est pas encodé en UTF-8. Il est encodé en $encodage.";
fi

TESTS SUR LA FONCTION CURL

Aujourd'hui, nous allons nous intéresser de plus près aux différents outputs produits par la fonction CURL.

On cherche notemment à détecter les erreurs possibles au moment de l'aspiration d'une page web afin de pouvoir prendre en compte ce facteur dans une des boucles de notre programme de traitement des URLS.

Pour cela, nous avons d'abord testé depuis le terminal différents cas :

    - Erreur de type "pas de connection possible !"
    - Erreur de type "adresse URL non valide !"

A) Erreur de type "pas de connection possible !"

On tape la commande suivante dans le terminal pour aspirer le contenu de la page web du cours :


curl http://www.tal.univ-paris3.fr/cours/masterproj.htm -o page-aspiree.html;


(l'option -o permet d'indiquer le fichier dans lequel sera enregistré le contenu aspiré par CURL)

Si tous se passe bien, le terminal affiche des informations sur l'aspiration de la page :


  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 99762  100 99762    0     0  1462k      0 --:--:-- --:--:-- --:--:-- 1709k


En revanche, si CURL rencontre un problème, il produit un message d'erreur. Par exemple, si on coupe notre connexion internet et qu'on reproduit l'expérience, on obtient le message suivant dans le terminal :

curl: (6) Could not resolve host: www.tal.univ-paris3.fr; nodename nor servname provided, or not known

Pour tester dans un script si CURL a fonctionné correctement, on peut donc enregistrer le message produit par CURL dans une variable qu'on appelera retourcurl: 


curl http://www.tal.univ-paris3.fr/cours/masterproj.htm -o page-aspiree.html;

retourcurl=$?;
echo "Voici la réponse de CURL : $retourcurl";


A partir de ce script, on constate donc que si $retourcurl vaut 0 alors, on peut dire que CURL a bien fonctionné. Si $retourcurl vaut 6 (ou une autre valeur?) alors il y a eu un problème. A partir de cela, on peut écrire un petit programme en bash testant cette condition sur un exemple :


#Aspiration d'une page web du cours
curl http://www.tal.univ-paris3.fr/cours/masterproj.htm -o page-aspiree.html;

#Capture du message produit par CURl dans une variable
retourcurl=$?;

#Test sur la valeur de cette variable
if [ $retourcurl == 0 ]
then 
echo "CURL a réussi à aspirer la page WEB.";
else 
echo "CURL a échoué.";
fi


Avec ce script, on obtientera dans le terminal un message nous indiquant si CURL a bien fonctionné sur la page web.

B) Erreur de type "l'adresse URL n'est pas valide !"

Lorsqu'on saisi une adresse URL qui n'est pas valide, le navigateur produit automatiquement un message d'erreur. Avec la commande CURL que nous avons tapé dans le terminal, si l'adresse n'existe plus ou bien qu'elle est incorrecte, on aura donc toujours une page HTML en sortie (affichant l'erreur 404). Du coup, dans notre script précédent, la condition $retourcurl == 0 sera bien remplie alors qu'il y a un problème !

Voici le résultat de la page asspirée lorsqu'on insère une erreur dans l'adresse URL du cours :


Et voici le code HTML de cette page :




Grace à la fonction egrep de bash, on peut donc parcourir le contenu du code HTML de la page aspirée afin d'y chercher une occurence de "Error 404" (caractérisitique d'un message d'erreur de ce type).

egrep -io "Error 404" page.html

(la fonction -i permet de ne pas prendre en compte la case et la fonction -o permet de chercher une occurence)

On va alors enregistrer la valeur produite par la fonction egrep dans une variable pour ensuite voir les valeurs qu'elle peut prendre.


#!/bin/bash
curl http://www.tal.univ-paris3.fr/cours/masterproj.htm -o page-aspiree.html;

error404=$(egrep -io "Error 404" page-aspiree.html);
echo "Voici le retour de la fonction egrep : $error404";


Lorsque l'adresse URL est correcte et que CURL fonctionne correctement, la variable error404 ne contient aucune valeur. Lorsque l'erreur 404 se produit, alors $error404 vaut "Error 404".

Voici un test en bash permettant de vérifier si la page aspriée contient une erreur de type 404 :

#!/bin/bash
curl http://www.tal.univ-paris3.fr/cours/masterpro.htm -o page-aspiree.html;

error404=$(egrep -io "Error 404" page-aspiree.html);

if [[ $error404 == "Error 404" ]]
then 
echo "La page aspirée contient une erreur de type 404.";
else 
echo "La page aspirée est valide.";
fi



Préparation d'un script test sur la fonction CURL

Grâce à ces petites expériences, on peut maintenant proposer un script testant le bon déroulement de l'aspiration d'une page web.

On veut d'abord tester s'il n'y a pas d'erreur de type A puis si c'est le cas, vérifier s'il n'y pas de problème de type B. On a donc le schéma de condition suivant :

Si A ne pose pas problème:
   alors on vérifie si B pose problème:
      si oui, on affiche que A fonctionne mais que B pose problème
      sinon, on affiche que tout fonctionne 
sinon on affiche que A pose problème

Pour mettre en oeuvre cela, on va simplement imbriquer les deux scripts test qu'on vient de présenter.

Voici une capture d'écran de notre nouveau script test :

On pourra maintenant reprendre ce script dans une des boucles de notre programme traitant nos fichiers d'URLS. On pourra aussi améliorer les performances de ce script en prenant en compte d'autres types d'erreurs que l'erreur 404...

jeudi 22 novembre 2012

INSTALLATION DE LYNX SUR MAC OS 10.8

Pour extraire les contenus textuels de nos pages web, nous avons utilisé un navigateur en ligne de commande : Lynx

La commande suivante permet par exemple de récupérer le contenu textuel de notre blog dans un fichier texte : 

lynx -dump -nolist http://les-manchots3.blogspot.fr > blog.txt


(L'option -dump sauvegarde le texte de la page visualisée dans le terminal et l'option -nolist permet de ne pas prendre en compte les références de liens)



Seulement, pour pouvoir utiliser cette superbe commande, il faut que Lynx soit installer sur votre ordinateur. Voici une manière de faire si vous travailler sur Mac. On a cherché pas mal de temps avant d'y arriver avec Mac Os 10.8 alors on espère que cette méthode pourra aider d'autres personnes...


Il faut d'abord télécharger et installer une version de Xcode qui soit compatible avec votre ordinateur. C'est gratuit sur Apple Store, si vous avez un compte. Ensuite, vous installerez Commande Line Tools for Xcode puis vous n'oublierez pas de signer la licence de Xcode en tapant dans votre terminal :

xcodebuild -license


(une fois que vous avez fait tout défiler, il faut taper 'agree' et valider)



Vous pouvez ensuite télécharger et installer la version de Macports dont vous avez besoin (pour Moutain Lion, Lion ou Leopard).


Une fois que vous avez fait tout ça, vous pouvez ouvrir votre terminal et taper cette commande pour installer Lynx :

sudo port install lynx

Normalment, si tout a bien fonctionné, vous devriez pouvoir accéder à notre blog depuis votre terminal avec la commande suivante :


lynx http://les-manchots3.blogspot.fr



Et voici ce que ça donne :




Magnifique n'est-ce pas ? Enfin, j'espère que vous préfèrez quand-même la mise en page de notre blog via votre navigateur !!!


mardi 20 novembre 2012

trois tableaux avec dump

Après de multiples tentatives et une patience qui nous surprend encore, nous sommes enfin parvenus à écrire un script contenant à la fois la boucle pour les différents fichiers d'urls, l'aspiration des pages et enfin le dump!!
Et oui, il fonctionne pour notre plus grand bonheur :)

Voici donc le script :




Et le résultat sur le web :




La prochaine fois nous essayerons de régler le problème de l'encodage qui sera très présent avec mes urls en arabe.
A bientôt donc, pour la suite de nos aventures!

Sofiane

dimanche 11 novembre 2012

Créer un tableau de liens avec des liens externes vers les pages visées et des liens internes vers les pages correspondantes aspirées

Nous voilà maintenant prêts pour la création d'un tableau d'URLs avec une colonne en plus contenant les pages aspirées de nos liens Urls.

Courageux que nous sommes, un dur labeur nous a donné ce résultat :

Voici le script amélioré :





On y a ajouté la commande Wget qui nous permet d'aspirer les liens de nos pages Urls. Les liens vers les pages aspirées sont ensuite mis dans une 3ème colonne !
Et ça marche :



Dans un prochain épisode : la même chose avec nos 3 fichiers d'URLs (français, anglais, arabe)

samedi 10 novembre 2012

TRAITEMENT AUTOMATIQUE DE PLUSIEURS FICHIERS D'URLS

Nous allons voir comment modifier ce script pour traiter de manière automatique plusieurs fichiers contenant des listes d'URLS.

PRINCIPE GENERAL

Le but est donc toujours de construire un tableau au format html qui recense nos URLs à partir d'un fichier texte, mais cette fois-ci on veut obtenir dans le même fichier .html autant de tableau qu'il y a de fichier .txt !

En programmation, ce type d'action peut être exécuté au moyen d'une boucle.

On sait qu'en bash la boucle for permet de traiter un ensemble d'éléments unité par unité. On va donc pouvoir créer un pseudo ensemble listant le contenu du dossier où sont classés tous nos fichiers d'URL (`ls URLS`) pour créer ensuite une boucle for où une unité correspondra à une ligne de cette ensemble, c'est à dire à un nom de fichier (for fichier in `ls URLS`).

On pourra ensuite insérer à l'intérieur de cette boucle, la partie du script précédent qui permettait de créer une ligne de tableau pour chaque nouvelle ligne contenu dans un fichier texte. Il faudra juste faire attention au nom des variables et à l'emplacement des balises html. Nous allons voir cela dans le paragraphe suivant.  

MODIFICATION DU SCRIPT ETAPE PAR ETAPE

Il va falloir commencer par changer l'input. On ne donne plus le nom d'un fichier, mais le nom d'un dossier. On utilise toujours la fonction echo et la fonction read. Le nom du dossier saisi sera enregistré dans une variable qu'on appelle dossier

echo "Donnez l'emplacement et le nom du dossier contenant les fichiers d'URLs : "; 
read dossier; 

Rien ne change en ce qui concerne le fichier Output :

echo "Donnez l'emplacement et le nom du fichier html qui présentera les urls dans un tableau : "; 
read tablo;

On enregistre ensuite dans le fichier Output le début du code html. Attention, on ne garde qu'une partie du code précédent puisque toutes les balises qui concernent les tableaux html seront insérées dans les boucles for. (Au fait: on ajoute aussi au passage un "x" à "tableau" dans le titre de notre future page html ;) !)

echo "<html><head><title>tableaux de liens</title></head><body>" > $tablo;

On ouvre ensuite une première boucle selon le principe vu plus haut. On appelle fichier la variable correspondant à une unité de notre ensemble, c'est à dire à un nom contenu dans notre dossier de fichiers d'URLs.

for fichier in `ls $dossier`
{

Pour chacun de ces noms de fichier, on enregistre dans le fichier Output les balises html ouvrant un nouveau tableau. Pour plus de clarté dans le résultat, on en profite aussi pour insérer avant un titre. (Pour faire simple on reprend simplement le nom du fichier traité, mais on pourrait améliorer ce titre avec l'utilisation d'une nouvelle variable et d'une incrémentation dans la boucle) :

echo "<p>Tableau $fichier</p>" >> $tablo;
echo "<table border=\"1\" width=\"50%\">" >> $tablo;  

Maintenant que les balises d'un nouveau tableau sont créées, on peut y insérer autant de lignes qu'il y d'adresses dans le fichier d'URLs qui est en train d'être traité. Pour cela, on reprend la boucle de notre premier script en indiquant cette fois-ci le chemin relatif vers les fichiers textes (puisqu'ils ne se trouve pas au même emplacement que notre script !). 

Une petite astuce : pour écrire ce chemin, on peut justement réutiliser les valeurs de nos variables. Dans notre cas, on a : $dossier = "nom du dossier contenant les fichiers d'URLS" et $fichier = "nom d'un fichier d'URLS". Du coup, $dossier/$fichier est un chemin relatif correct.

i=1;
for nom in `cat $dossier/$fichier`
{
echo "<tr><td align=\"center\" width=\"20\">$i</td><td align=\"center\"><a href=\"$nom\">$nom</a></td></tr>" >> $tablo;
let "i+=1"; 
}

Une fois cette boucle terminée, on ferme la balise html du tableau correspondant au fichier qui a été traité dans la boucle :

echo "</table>" >> $tablo;

On est alors près pour reprendre la première boucle sur le fichier d'URLs suivant. Pour indiquer cela, on ferme  l'accolade correspondant à la première boucle :

}

Une fois que tous les fichiers d'URLs ont été traités, il ne manquera plus qu'à fermer les balises html restées ouvertes, c'est à dire <body> et <html> :

echo "</body></html>" >> $tablo; 

Notre fichier html en Output est alors valide. Notre script est terminé !


Voici maintenant ce que ça donne en images :

Le nouveau script avec #commentaires

Affichage de l'organisation des fichiers et exécution du script dans le terminal

Extrait du fichier HTML en Output

Affichage du résultat dans Safari


AUTRE SOLUTION POUR SUIVRE l'EXECUTION D'UN SCRIPT CONTENANT UNE BOUCLE

En reprenant comme base le script de la deuxième méthode vu en cours pour créer un tableau html à partir des lignes d'un fichier texte, la proposition d'utiliser les fonctions echo et read pour suivre étape par étape l'exécution d'une boucle est valable !

L'exemple en image :

Script de la deuxième méthode avec ajout des fonctions echo et read

Vu du terminal à l'exécution du script


ASTUCE POUR SUIVRE DANS LE TERMINAL L'EXECUTION D'UNE BOUCLE [PROBLEME!]

Aujourd'hui, nous sommes à la recherche d'un moyen qui nous permettrait de suivre l'exécution d'un script traitant un nombre important de données via une boucle.

Dans le cas de nos URLs, nous souhaitons par exemple savoir à chaque fois qu'une nouvelle ligne a bien été créée dans notre tableau.

Pour cela, nous pouvons ajouter une fonction echo dans la boucle, par exemple :

echo "L'URL $url a bien été traitée.";

De cette manière, la liste de toutes les URLs traitées s'affiche dans le terminal au moment de l'exécution du script.

Voici un exemple en image :

Exemple de script avec ajout d'une fonction echo


Vu dans le terminal après l'exécution du script


Pour créer une interaction avec l'utilisateur, on peut exploiter une des caractéristiques de la fonction read qui après avoir lu une donnée attend une validation par ENTER avant de poursuivre le script.

On pourrait donc enrichir le script précédent comme ceci :

echo "L'URL $url a bien été traitée. Appuyer sur ENTER pour continuer.";
read;

Voici ce qu'on obtient :

La liste d'URLs à traiter (test)


Le nouveau script

Exécution du script dans le terminal

Le tableau html en output


D'après ces captures d'écran, on voit que notre programme n'a pas fonctionné comme on l'aurait souhaité. On constate que :

- Il n'y a plus eu d'interaction dans le terminal avec l'utilisateur dès que le nom du fichier output a été saisi.
- Seules la première et la troisième ligne du fichier urls.txt ont été insérées dans le tableau !

Si vous avez une explication pour ce mystérieux résultat ou bien une autre solution, vous pouvez laisser un message aux Bras Cassés dans les commentaires :) !





lundi 5 novembre 2012

INPUT ET OUTPUT... EN IMAGES !

Pour être sur que nos scripts fonctionnent bien, nous avons quand-même fait quelques petits tests...

Voici ce que ça donne en images :

Tout d'abord l'exécution du script correspondant au premier objectif (nous travaillons dans un dossier test : kamikaz !) :


On voit bien dans cette capture d'écran du terminal, qu'après avoir lancé le script et indiqué l'input et l'output, un nouveau fichier .html est créé dans notre dossier kamikaz.

Voici ce que contenait notre fichier .txt (input) :



Et voici ce que contient le nouveau fichier .html (output) :


Et lorsqu'on ouvre ce fichier avec Safari, on obtient notre premier tableau : objectif 1 accompli !


Voici maintenant les captures d'écran présentant les résultats obtenus avec les scripts des objectifs 2a (numérotation) et 2b (liens hypertextes) (on garde le même input teste)


objectif 2a accompli :) !



Objectif 2b accompli :) !!!


EXERCICES POUR LE 7 NOVEMBRE

Voici aujourd'hui quelques captures présentant nos travaux pour la séance du 7 novembre !

Rappel : Après avoir créer notre environnement de travail à l'aide d'un script bash et recueilli les URLs qui constitueront notre corpus, nous allons maintenant écrire un script qui répertorie dans un tableau HTML l'ensemble de nos URLs.

Comme c'est encore le début, nous avançons par petite étape...

Objectif 1 : Lire un fichier contenant une liste d'URLs et produire un fichier HTML contenant un tableau à une colonne regroupant ces URLs

Pour cela, nous avons repris une des deux méthodes vues lors de la dernière séance. Cette fois voici, nous avons inséré nos commentaires directement dans le script ;) !

script objectif 1: tableau des URLs

Objectifs 2a : Il s'agit d'améliorer un peu l'aspect de ce tableau en ajoutant une colonne avec le numéro des URLs... histoire de ne pas avoir à les compter à la main :) !

Pour cela, on a enrichit le script précédent d'une nouvelle variable... (voir nos commentaires dans le script)

script objectif 2a: tableau des urls + numéro

Objectifs 2b : On améliore encore un peu notre tableau d'URLs en activant les liens hypertextes vers les sites webs correspondants.

script objectifs 2a: tableau des urls + numéro + liens hypertextes