Robot de recherche WEB

Projet Internet

réalisé par Abdelmalek Hamou.


  1. But du projet
  2. Constitution du projet
  3. Le Crawler
  4. Base de données


1) But du projet


Le but du projet est d'implémenter un robot de recherche Web de type Google simplifié. Le robot comporte trois parties : (1) le crawler(araignée) qui explore les pages HTML du WEB afin de constituer (2) la base de données liant les URI aux mots qui les décrivent et (3) le module d'interrogation qui cherche dans la base des mots et affiche les URIs correspondants.

2) Constitution du projet


1) Constitution du projet :

Le projet est constitué de plusieurs répertoires :


Les Pages du projet (dans les repertoires HTML et CGI-BIN) :

Page à partir de laquelle l'utilisateur pourra effectuer une recherche. Pour cela, il lui suffit d'entrer un mot et d'appuyer sur "entrer" pour lancer la recherche. L'utilisateur pourra choisir si il souhaite rechercher une page web, une image ou une adresse mail.
Cette page permet également d'accéder à la documentation mais aussi de consulter ou modifier le fichier de configuration "crawler.conf" qui se trouve dans le répertoire SRC.
La page "recherche.php3" va effectuer un accès à la base de données. Elle va rechercher les mots clés qui correspondent (complètement ou en partie) à l'entrée de l'utilisateur.
La page va afficher les résultats à la suite sous la forme d'un tableau avec 2 colonnes. La première contient le lien vers l'URI trouvée et la deuxième contient le mot clé, relié à l'URI, que l'utilisateur à recherché.
Si aucun mot n'est trouvé dans la base, le crawler informe qu'aucun résultat n'a été trouvé.
Cette page lit et affiche les informations contenues dans le fichier de configuration (racine et profondeur).
Un lien permet d'accéder à la page modifierconf.php3 qui permet de modifier les paramètres de configuration.
Cette page est utilisé pour pouvoir modifier les paramètres du fichier de configuration.
L'utilisateur entre, dans les champs "URL Racine" et "Profondeur", les nouvelles valeurs qui seront utilisées pour une future recherche.
Cette page envoie les nouveaux paramètres à la page "newparametres.php3" à l'aide de la méthode "POST".
Cette page va écrire, dans le fichier "crawler.conf", les paramètres qu'elles auront reçu.
La page nous confirme que les nouveaux paramètres ont bien été pris en compte.
Un lien nous propose d'effectuer une nouvelle recherche.


2) Mise en place :

Le projet a été réalisé avec le langage java, HTML, PHP et une base de données MySql.

Le crawler a été écrit avec le logiciel EasyPHP 1.7 sur Windows qui regroupe :


EasyPHP 1.7 est téléchargable sur le site de 01net.com à l'adresse : http://telecharger.01net.com/windows/Internet/serveur_ftp/fiches/14744.html.

Il est important d'exécuter le crawler sur Windows car lors de la création, l'écriture ou la lecture des fichiers de configuration ou de journalisation, le saut de ligne est effectué par le symbole "\r\n". Sur Unix, le symbole correspondant au saut de ligne est "\n". Donc cela peut provoquer des problèmes lors de la manipulation de fichiers.


3) Execution :

Il n'y a pas de répertoire MAKEFILE. Pour la compilation du projet, il suffit d'aller dans le répertoire SRC, la où se trouve les fichiers java.
La commande : javac *.java permettra la compilation.
Pour pouvoir lancer le crawler, il suffira de faire la commande suivante :
java MonCrawler < fichier de configuration > <--options>

Remarque:
Je n'ai pas totalement fini le projet : les options --img et --mail ont été réalisé mais elles posent certains problèmes lors du parcours. L'option --cond n'a quand à elle pas été réalisé. Seul l'option --init fonctionne correctement.

Autre remarque importante:
Il est important que lors de la consultation ou de la modification du fichier de configuration à l'aide des pages dans le répertoire CGI-BIN, la répartition des répertoires ne soient pas changés. Cela parce que lors de la consultation ou de la modification des configurations, on utilise un chemin relatif pour acceder au fichier de configuration : ../SRC/crawler.conf.

3) Le Crawler


Nous allons expliquer ici le fonctionnement de notre crawler.

Le crawler contient 4 parties :


1) L'initialisation :

Notre programme va tout d'abord vérifier que les arguments entrés par l'utilisateur sont corrects. L'utilisateur doit impérativement mettre en argument le fichier de configuration. Il peut aussi ajouter les options souhaitées. Comme dit précedemment, l'option "--cond" n'a pas été implémentée. De plus, les options "--img" et "--mail" posent certains problèmes que je n'ai pas réussi à régler.

Le crawler va lire les informations contenues dans le fichier de configuration. Il va ensuite créer le fichier de journalisation "crawler.log".
Le lancement du parsage commence par l'appel à la fonction "Exlpore()".

2) Les classes de vecteurs :

Nous avons défini 3 vecteurs :

1) tab va contenir toutes les URI parcourues. Il aura, pour chaque URI, 3 informations ( voir la classe AdresseURI ):

2) vecteurLiens va contenir les adresses des pages parsées. Au fur et à mesure du parsage, des pages vous être ajoutées et d'autres retirer du vecteur. Ce vecteur va nous permettre de prendre en compte la profondeur du parcours (voir fichier de configuration) et donc de nous arrêter à la profondeur souhaitée.

3) PageDejaVisitee va contenir les adresses des pages parsées. Ce vecteur va être utilisé pour nous permettre d'éviter de parser une page déjà visitée.

3) Le parcours des pages HTML :

Le Crawler va commencer par parcourir la racine à l’aide de la classe « CrawlerCallbackHandler » (voir fonction Explore()).

//Parcours de la racine
CrawlerCallbackHandler handler = new CrawlerCallbackHandler(tab,vecteurLiens,PageDejaVisitee,urlChemin,optionImage,optionMail); new ParserDelegator().parse(rdr, handler, true);

« CrawlerCallbackHandler » est une classe fille de HTMLEditorKit.ParserCallback qui va redéfinir les méthodes handleStartTag(), handleEndTag(), handleSimpleTag() et handleText().
Ces méthodes servent au parcourt d’une page HTML. Elles vont permettre d'insérer, dans les différents vecteurs définis plus haut, les informations sur les pages visitées.
Les informations receuillies sur le vecteur "TabURI tab" vont être insérées dans la base de données et dans le fichier de journalisation "crawler.log".

On fait appel maintenant à la fonction récursive "private void parcoursLargeur()".

Cette fonction va effectuer les même opérations que la fonction "Explore()". Elle va simplement parser la première de page qui se trouve dans "vecteurLiens". Elle fera appel à la classe « CrawlerCallbackHandler » comme pour la fonction "Explore()". Puis elle insérera les informations recueillies dans la base de données et dans le fichier de journalisation à partir des vecteurs.
Pour finir, elle fera un appel à elle-même, c'est-à-dire un appel récursif.

La difficulté ici sera de faire en sorte que la parsage s'arrête à la profondeur décrite dans le fichier de configuration.

Comment vecteurLiens permet le parcours en largeur sur une profondeur p :

En faite, voila comment on va pouvoir faire un parcours en largeur sur une profondeur p :
A l'entrer de la fonction "Explore()", vecteurLiens est vide. On va insérer la racine dans ce vecteur. Puis on insère le caractère "*". Ce caractère symbolise le fait qu'on change de profondeur. Ensuite on fait appel à la fonction "CrawlerCallbackHandler (TabURI tab, TabPageAPaser vecteurLiens, TabLiensVisites PageDejaVisitee, String cheminRacine, boolean optionImage, boolean optionMail)" qui va parser la page racine. Elle va insérer les pages fils de la racine dans vecteurLiens.

On entre maintenant dans la fonction de parcours récursive "parcoursLargeur()". Cette fonction va supprimer le premier élément de vecteurLiens (c'est-à-dire la racine). Le premier élément du vecteur est maintenant "*". Le vecteur contient aussi tous les fils de la racine. On va donc baisser de 1 la variable de profondeur et insérer l'élément "*" pour signifier que les prochains éléments qui seront ajoutés auront une profondeur égale à +1 celle de leur prédécesseurs. On va ensuite parser tous liens contenus dans le vecteur jusqu'à rencontrer le symbole "*". La fonction récursive "parcoursLargeur()" s'arrête lorsque la variable profondeur est inférieure à 0.

Cela fonctionne car on sait que toutes les pages d'une profondeur p se trouvent à la suite et sont terminé par l'élément "*". A chaque fois que l'on va parser une page de la profondeur p, on va ajouter ces liens derrière le symbole "*" pour dire que leur profondeur est supérieure de 1. Lorsque l'on aura visité la dernière page de profondeur p, on ajoutera le symbole "*" derrière ces liens fils. On pourra alors commencer le parsage des pages de profondeur p+1 et ajouter leurs liens fils derrière le dernier symbole "*" ajouté. On répète l'opération jusqu'à ce que la profondeur soit atteinte.

4) L'enregistrement des informations :

Après le parsage d'une page HTML, le crawler enregistre, dans la base, les informations (mots clés, type) des liens de cette page. Le crawler enregistre toutes les informations dans le fichier de journalisation à l'aide des méthodes "FichierJournalisation(String msg)" et "AfficheFichierJournalisation()" (voir classe MonCrawler.java).

4) Base de données


Elle sert de tampon de communication entre le crawler et le module d’interrogation. Le crawler enrichit cette BD avec les informations recueillies sur les URIs explorées.
Nous avons besoin de 3 tables pour notre projet :


Voici comment ont été réalisé les tables ( voir répertoire SQL ) :

CREATE TABLE `motclef` (
`id_motclef` int(16) unsigned NOT NULL auto_increment,
`motclef` varchar(32) NOT NULL default '',
PRIMARY KEY (`id_motclef`),
UNIQUE KEY `mot` (`motclef`)
);

CREATE TABLE `uri` (
`id_uri` int(16) unsigned NOT NULL auto_increment,
`uri` varchar(64) NOT NULL default '',
`type` varchar(8) NOT NULL default '',
PRIMARY KEY (`id_uri`),
UNIQUE KEY `uri` (`uri`)
);

CREATE TABLE `uri_motclef` (
`id_uri` int(16) NOT NULL default '0',
`id_motclef` int(16) NOT NULL default '0',
PRIMARY KEY (`id_uri`,`id_motclef`)
FOREIGN KEY (id_uri) REFERENCES uri (id_uri),
FOREIGN KEY (id_motclef) REFERENCES motclef (id_motclef)
);

Nous allons décrire les principales méthodes pour la gestion de la base de données :

Cette méthode permet d’ouvrir une connexion à la base de données.
Cette méthode ferme la connexion ouverte à la base de données.
Méthode qui va vider la base de données.
Il s’agit de la méthode principale pour la gestion des insertions dans la base. Cette méthode est appelée après chaque parsage.
Les liens trouvés lors du parsage seront contenus dans « TabURI tab ». Notre méthode va, pour chaque URI contenu dans « TabURI tab », insérer les informations recueillis dans la base de données.
Méthode qui va insérer dans la base l’URI contenu dans « adresse » donnée en paramètre.
Elle retourne l’identifiant de la base où l’URI à été insérée. Pour cela elle fait appel à la méthode « recupIdURIBDD(adresse.getAdresseUri()) ».
Méthode qui va chercher, dans la base, l’URI donné en paramètre.
Si elle le trouve, elle retourne l’identifiant de cet URI sinon elle retourne –1. La méthode doit, sauf en cas d’erreur, trouvé l’URI donné en paramètre car cette méthode est appelée après « insertionURIBDD(AdresseURI adresse) » qui elle a insérée l’URI que « recupIdURIBDD(String uri) » va chercher.
Méthode qui insère, dans la base, le mot donné en paramètre.
Elle retourne l’identifiant de la base où le mot à été inséré. Pour cela elle fait appelle à la méthode « recupIdMotBDD(String mot) ».
Méthode qui fonctionne de la même manière que « recupIdURIBDD(String uri) ».
Elle va chercher, dans la base, le mot donné en paramètre.
Si elle le trouve, elle retourne l’identifiant du mot sinon elle retourne –1. La méthode doit, sauf en cas d’erreur, trouvé le mot donné en paramètre car cette méthode est appelée après « insertionMotBDD(String mot) » qui elle a insérée le mot que « recupIdMotBDD(String mot) » va chercher.
La fonction permet le lien entre une URI et ses mots clefs dans la base de données.
Elle prend en argument l’identifiant de l’URI insérée et l’identifiant des mots clefs qui correspondent à l’URI. Ces identifiants ont été récupérés grâce aux méthodes ci-dessus.
Méthode qui prend en argument la chaîne de caractères (les mots clés) d'une adresse. Elle va, à partir de cette chaîne, découpé chaque mot et le mettre dans un vecteur.
A la fin, elle retourne le vecteur qui permettra d'insérer chaque mot indépendamment tout en le faisant correspondre à une adresse URI.


Les accès à la base :

La fonction public void insertionBDD() est celle qui s'occupe des insertions. Donc, à part la méthode ConnectionBDD() qui fait une connexion à la base au début du lancement et la méthode deconnectionBDD() qui se déconnecte, c'est la méthode insertionBDD() qui fera la plupart des accès à la base.

Voici comment se présente la fonction "insertionBDD()" ( voir MonCrawler.java ):

	//Fonction qui gere les insertions dans la base de données.
	public void insertionBDD() {

		int idURI, idMot;
		String leMotCourant = null;
		Vector lesMots=null;
		int taille,i;

		Object obj;

		AdresseURI adresse;

		try {

			taille = tab.size();

			//Pour chaque URI contenu dans "tab", on insere ses 
			//informations dans la base de données.
			for (i=0 ; i
Donc pour chaque page parsé, on va faire : 1 accès pour insérer l'URI, n accès ( avec n inférieur aux nombres de pages parséss pour l'option --init) pour chercher l'identifiant de l'URI insérée, puis pour chaque mot clé d'une URI on fait un accès ( p accès ), puis on recherche l'identifiant du mot inséré ( maximun n.p accès ) et pour finir on fait un accès pour faire le lien entre les 2 tables.

On peut en conclure que les accès à la base pour le parsage des pages HTML est de l'ordre de n*n*p au maximum.