Wiki du Navicrawler
Bienvenue sur ce bloc-notes qui s'enrichira petit-à-petit de conseils, tutoriels et autres aides qui figureront ici avant d'intégrer les releases "officielles" de documentation.
Les Heuristiques
A quoi ça sert ? Comment ça marche ?
Les heuristiques sont un moyen de récupérer de l'information dans des sites qui sont toujours construits de la même façon. Il s'agit typiquement des "plateformes" comme Skyblog, Flickr, YouTube... Dans ces sites certaines pages sont toujours construites de la même façon. Comme il y a une règle sous-jacente qui définit la structure de ces pages, il est possible de récupérer du contenu "à la chaîne". Les heuristiques servent spécifiquement à ça : elles permettent de cibler du contenu qui sera récupéré dans le Navicrawler.
Exemple : avec une heuristique appropriée, on peut récupérer le profil d'une vidéo YouTube : le nom de la vidéo, sa note, le nombre de commentaires, le nombre de fois qu'elle a été visionnée... L'heuristique permet de récupérer ces informations sur TOUTES les vidéos YouTube
Les heuristiques permettent donc de récupérer du contenu formaté, et de le stocker dans le Navicrawler. Elles ont donc ceci de particulier qu'elles utilisent la "façon de penser" du Navicrawler : le graphe. Ceci signifie que les données récupérées peuvent être transformées en noeuds (et leurs propriétés) ou en arcs. Autrement dit on peut récupérer des données relationnelles. Un exemple sera plus parlant...
L'heuristique YouTube récupère deux genres de profils différents
- Le profil des vidéos (url, nom, note, nb. commentaire, nb. de visionnages
- Le profil des membres (url, pseudo, âge, pays, descriptif, site web, nb. de vidéos postées, nb. d'amis
Comme on le voit, chaque profil a différentes propriétés associés. Mais en plus de ces données, l'heuristique récupère le lien qu'il y a entre un membre du site et une vidéo qu'il a postée
Résumons. Une donnée récupérée (un champ de texte) peut être utilisé pour définir :
- une entité (un noeud, ex. : une video YouTube)
- une propriété (d'un noeud, ex. : la note d'une vidéo)
- un lien (entre deux noeuds, ex. : entre un membre et une vidéo)
Naturellement on peut déclarer plusieurs types de liens. Quand on étudie des blogs, ce n'est pas la même chose d'avoir un lien dans le blogroll ou d'avoir un lien dans les commentaire... Sans compter qu'il peut y avoir un lien entre un site et une entité... Encore une fois, illustrons ceci :
L'heuristique YouTube récupère en fait différents liens :
- Le lien "posté par", qui lien une vidéo à celui qui l'a postée (video > membre)
- Le lien "a posté" : le même mais en sens inverse (membre > video)
- Le lien "a commenté" (membre > video)
- Le lien "a commenté" (membre > membre) car on peut commenter un profil !
- Le lien "est ami de" (membre > membre)
- Le lien "a le site web" (membre > site web)
Dans le Navicrawler, les heuristiques peuvent ou non être mélangées avec le graphe des sites web. C'est pour ça qu'il est possible de créer des liens entre des entités et des sites web. Et c'est pour ça qu'en fait les heuristiques se manipulent de la même façon que le reste du corpus.
Autrement dit, pour exporter une heuristiques il y a deux possibilités. Soit on exporte le graphe, soit on exporte en forme de tableau. Dans les deux cas, il suffit de cocher la case "Entités & Props. Heuristiques" dans l'onglet "File" du Navicrawler. Si l'on souhaite uniquement récupérer les heuristiques, alors il faut décocher les autres cases, qui correspondent aux sites.
Accéder à l'édition d'une heuristique
- Afficher l'onglet "heuristiques" dans le Navicrawler. Pour ce faire il suffit de cocher la bonne cas dans l'onglet "(+)" du Navicrawler.
- Cliquez sur le bouton "Editer" en bas de l'onglet "Heur." et sauvegardez le fichier quelque part, sur le bureau par exemple.
- Ouvrez le fichier dans un éditeur de texte, Notepad++ par exemple
- Editez les heuristiques (cf. plus bas), sans oublier de sauvegarder !
- Dans l'onglet "Heur." du Navicrawler, cliquez sur "Importer" et importez le fichier d'heuristiques que vous venez de modifier
- Si vous avez créé une heuristique, n'oubliez pas de la cocher dans la liste, pour qu'elle soit active
- Rechargez la page (F5 sur PC) pour appliquer les heuristiques. Pas besoin de redémarrer le Navicrawler, ça marche dès l'import effectué.
Comprendre le XML des heuristiques dans les grandes lignes
Le fichier que vous pouvez exporter pour éditer les heuristiques est un XML. Respectez la syntaxe XML et vous vous épargnerez un débogage douloureux.
Nous supposons ici que vous disposez des notions de base en XML, et que vous maîtrisez pas trop mal les expressions régulières. Si ce n'est pas le cas, faites-vous les dents sur un tutoriel comme on en trouve des tombereaux sur le web.
En outre, il est indispensable de disposer du DOM Inspector dans Firefox. Si vous n'avez pas cette extension, téléchargez-la. Elle est facile à trouver.
Ces précautions prises, ouvrons le fichier XML des heuristiques, que vous avez exporté quelque part sur votre disque dur, et observons comment il est fait.
- Au premier niveau d'arborescence, le fichier contient des noeuds filter : chaque noeud filter définit un filtre. Il y aura donc autant de filtres dans la liste du Navicrawler que de noeuds filtres dans ce fichier.
- Chaque noeud filter a un attribut "label" : c'est le nom du filtre qui apparaîtra dans la liste.
- De même l'attribut "date" doit être mis à jour à chaque modification. En effet les sites évoluent et les heuristiques se périment. Si vous utilisez une heuristique qui ne marche pas, c'est qu'elle est périmée : vérifiez la date. Si vous voyez une heuristique ancienne, il est probable qu'elle ne marche plus.
- Le premier sous-noeud du "filter" est le "domain_template". Autrement dit ce noeud définit à quelles pages l'heuristique s'applique.
- Les sous-noeuds suivants définissent les informations qui seront récupérées et ce qu'il en sera fait
Nous allons maintenant approfondir ces deux derniers points pour pouvoir éditer concrètement ces heuristiques.
Le Domain Template
D'abord, une remarque importante.
Chaque heuristique analyse un type de page donné. Autrement dit si vous voulez décortiquer plusieurs types de pages, il vous faut plusieurs heuristiques. Par exemple il faut une heuristique pour les pages de membres de YouTube, et une autre heuristique pour les pages de vidéo. Même si c'est le même site !
La limitation est simple. Il n'y a qu'un seul "domain_template" par filtre d'heuristique. Comme on l'a vu, le domain_template définit à quelles pages s'applique le filtre. C'est-à-dire que le filtre marche ainsi :
- Je regarde si la page est concernée (dans le domain_template)
- Si oui, alors je récupère toutes les infos demandées.
Chaque filtre a UN et un seul domain_template, même si on récupère au passage plusieurs informations. Car comme on verra plus tard on ne peut pas récupérer n'importe quoi sur n'importe quelle page. Il faut donc que le domain_template définisse clairement les pages sur lesquelles la récupération d'infos marche bien.
Mais qu'est-ce que le domain_template ? C'est une expression régulière. C'est une façon de dire, par exemple :
Récupère-moi toutes les pages qui s'écrivent "http://www.youtube.com/user/ " suivi de n'importe quoi.
Dans ce cas, le filtre d'heuristique s'appliquera à toutes les pages qui correspondent à la règle, par exemple http://www.youtube.com/user/Wes14 est concernée, mais pas http://www.youtube.com/watch?v=5lZAJaKkFg8 et c'est tant mieux : car la première page est celle d'un profil tandis que la dernière est celle d'une vidéo. Or le but ici est justement de définir juste les pages de profil, pour pouvoir les récupérer.
Nous n'allons pas expliquer les regexp en détail, mais nous allons tout de même présenter quelques détails.
- D'abord, "n'importe quoi" s'écrit ".* " (point étoile) en regexp. "Nicolas est .* " va marcher pour "Nicolas est président", "Nicolas est teigneux" mais pas pour "Ségolène est présidente". Pourquoi ? Le point "." signifie "n'importe quel caractère". Donc "p.p." va marcher pour "papi", "papa", "pépé" mais pas pour "poupi" car "ou" fait deux caractères. Ensuite, l'étoile "* " signifie "autant de fois qu'on veut". Par exemple "bla* " va marcher pour "blabla", "blablabla", mais pas pour "blablabli". Donc ".* " veut dire "n'importe quel caractère, autant de fois qu'on veut", donc "n'importe quoi".
- A ce stade vous avez complètement compris le principe. Sauf qu'il ne faut pas rater l'obligation de déspécifier les caractères spéciaux. Car ce qu'on a envie d'écrire, c'est ça : "youtube.com/user/.* " sauf que comme on a vu plus haut, le point "." signifie "n'importe quel caractère", donc l'heuristique va marcher pour "youtubeZcom/user/blabla" par exemple. Vous me direz, on s'en fiche ça n'existe pas... Sauf que le caractère slash "/ " signifie lui-aussi quelque chose ! Bref, pour s'en sortir, il faut déspécifier les caractères spéciaux. Il faut expliquer que le point dans "youtube.com" est un vrai point, et pas le point qui veut dire "n'importe quel caractère". Pour déspécifier un caractère, il faut rajouter un antislash "\" devant. Donc le "vrai" point s'écrira "\." et le "vrai" slash s'écrira "\/" (antislash slash).
Donc si on remet les étapes dans l'ordre, ça donne ça :
- On définit intuitivement la règle : "http://www.youtube.com/user/" suivi de n'importe quoi.
- On déspécifie les caractères spéciaux en rajoutant un antislash avant : "http:\/\/www\.youtube\.com\/user\/" suivi de n'importe quoi
- On rajoute les caractères spéciaux pour traduire l'expression en expression régulière : "http:\/\/www\.youtube\.com\/user\/.*"
- Notre noeud domain_template dans le XML s'écrit donc :
"<domain_template>http:\/\/www\.youtube\.com\/user\/.*</domain_template>"
Et voilà !
Encore un exemple pour la route. Cette fois-ci on n'utilisera pas l'étoile, mais le plus "+" qui veut dire "n'importe quel nombre de fois mais au moins une fois". Autrement dit, le point plus ".+" veut dire "n'importe quoi mais pas rien".
Notre exemple, ce sont les pages myspace. On voudrait ceci :
http:// suivi de n'importe quoi, puis .myspace.com/ suivi de n'importe quoi mais pas rien.
Ce qui au final se traduit ainsi :
"<domain_template>http:\/\/.*\.myspace\.com\/.+</domain_template>"
Le but de ce domain template est de récupérer les pages des profils myspace. Regardons ce qui se passe :
- http://www.myspace.com/thedoband passe bien : c'est ce qu'on voulait (c'est la page du groupe "the do").
- http://myspace.com/thedoband marche aussi : c'est la même page mais on la veut quand même (les www sont optionnels).
- par contre http://www.myspace.com/ ne marche pas, et tant mieux car c'est une page générique (pas un profil).
Les Define Element : introduction
Et nous voilà au coeur du sujet, dans la partie difficile...
Rappel : les filtres se définissent en XML sous la forme suivante :
<filter ...> <domain_template> ... </domain_template> <define_element> ... </define_element> <define_element> ... </define_element> ... </filter>
- Chaque noeud define_element décrit un élément à récupérer dans la page.
- Chaque define_element a un type qui spécifie ce qu'on fait de ce qu'on récupère : une entité, une propriété, un lien...
- Les sous-noeuds du define_element définissent ce qu'on récupère. Mais ATTENTION : selon le type, on ne récupère pas les mêmes choses, donc les sous-noeuds dépendent du type du define_element. Par exemple, pour une entité on récupère une seule chose, tandis que pour un lien on récupère deux choses, la source et la destination du lien !
On va commencer simplement avec un exemple de filtre simpliste. Voici son code, qu'on va expliquer petit à petit :
<filter label="ToutesImages" date="11 avril 2008">
<domain_template>http:\/\/.+</domain_template>
<define_element label="Image" type="node">
<target_node_template>theNode.nodeName=="IMG"</target_node_template>
<grab>theNode.src</grab>
</define_element>
</filter>Avec ce qu'on a vu avant, on peut déjà dire les choses suivantes :
- Ce filtre s'appelle "ToutesImages" et il a été écrit le 11 avril 2008.
- Il s'applique aux pages qui s'écrivent "http:// " suivi de n'importe quoi mais pas rien ; autrement dit il s'applique à toutes les pages.
- Il récupère une seule chose, car il n'a qu'un seul define_element.
Intéressons-nous à ce define_element :
<define_element label="Image" type="node"> <target_node_template>theNode.nodeName=="IMG"</target_node_template> <grab>theNode.src</grab> </define_element>
La première chose à voir, c'est le type. Le type est "node" : on verra plus loin ce que ça veut dire. Disons que c'est le type le plus simple à écrire. Dans ce type il y a les deux sous-noeuds les plus basiques qu'on trouve dans tous les autres types de define_element : le target_node_template et le grab.
Il faut maintenant parler du DOM, mais pour l'instant nous allons seulement voir le minimum vital. Chaque page a une sorte de squelette appelé DOM, et c'est là-dedans qu'on va chercher l'information. Ce qu'il y a dans le target_node_template et le grab permet d'aller dans le DOM chercher ce qu'il faut.
- Le DOM ou "Document Object Model" est une arborescence qui contient tout ce qu'il faut pour que le navigateur affiche la page.
- Le DOM n'est pas le code source. Il est calculé grâce au code source mais il est plus pratique à manipuler.
- Le DOM existe en mémoire pour toutes les pages non défectueuses. On peut le voir avec l'extension DOM Inspector pour Firefox (mais le DOM existe aussi dans tous les autres navigateurs).
- Chaque "chose" de la page est un noeud dans le DOM. Les images sont des noeuds, les paragraphes sont des noeuds, les textes sont des noeuds, les blocs sont des noeuds... Chaque élément est un noeud.
- Les noeuds du DOM peuvent avoir des "enfants", qui sont des noeuds aussi (des sous-noeuds). Un paragraphe est un noeud, il a des enfants qui sont par exemples du texte ou des images. Il peut aussi avoir des "parents" qui sont des blocs qui le contiennent, etc.
- Et les noeuds ont tous des prioriétés : un nom, une classe, d'autres choses encore... Par exemple une balise de lien "< a href="monsite.com">Mon Site</a>" est un noeud, de nom "A" avec un attribut "HREF" qui vaut "monsite.com".
Le taget_node_template fonctionne ainsi : le filtre va passer sur tous les noeuds du DOM de la page, et va s'arrêter uniquement sur les noeuds définis par le target_node_template. Voici ce qu'il y a dans le target_node_template dans notre exemple :
theNode.nodeName == "IMG"
Quand le filtre passe sur un noeud, celui-ci s'appelle "theNode". Ici on recherche donc les noeuds dont le nom (le "nodeName") est égal à "IMG". Concrètement, ces noeuds sont les images. Ici on recherche toutes les images.
Le grab fonctionne ainsi. Lorsque le filtre s'est arrêté sur un noeud (car il est OK pour le target_node_template), le grab définit ce qu'il faut récupérer. ATTENTION, les heuristiques ne récupèrent que du texte ! On ne va donc pas récupérer l'image. Non, ici on va récupérer l'adresse de l'image. Car voici ce qu'il y a dans le grab :
theNode.src
Il faut savoir que l'attribut "src" (source) d'une image définit l'URL où on peut télécharger cette image.
Pour résumer, ce filtre fonctionne ainsi :
- recherche tous les noeuds DOM de nom IMG
- récupère leur URL (attribut "src").
REMARQUE IMPORTANTE : ici on récupère plusieurs éléments dans chaque page ! On récupère autant d'images qu'il y en a. Mais pour cela un seul define_element suffit.
Il est temps d'expliquer le type "node" du define_element de notre exemple. Petit rappel : quand le Navicrawler va sur un site, il crée un noeud de graphe en mémoire. Quand il trouve des liens hypertexte, il crée des arcs de graphe en mémoire. Eh bien c'est toujours pareil avec cette heuristique. Quand il va trouver une image, il va créer un noeud de graphe propre à cette image, et il va créer un arc de graphe entre cette image et le site où elle se trouve.
Pour résumer cette section, revenons sur le code XML du filtre :
<filter label="ToutesImages" date="11 avril 2008">
<domain_template>http:\/\/.+</domain_template>
<define_element label="Image" type="node">
<target_node_template>theNode.nodeName=="IMG"</target_node_template>
<grab>theNode.src</grab>
</define_element>
</filter>- Ce filtre s'appelle ToutesImages. Si on coche "ToutesImages" dans le Navicrawler, il se mettra en route.
- Le domain_template va marcher pour toutes les pages en "http". Donc le filtre va s'activer tout le temps.
- Dans chaque page, il va rechercher les noeuds DOM de nodeName "IMG", et récupérer leur attribut "src" : il récupère l'URL des images de chaque page.
- A chaque fois, le filtre crée un nouveau noeud de graphe, de type "Image" (car c'est le label du define_element) qui se nommera d'après l'URL de l'image (car les define_element de type "node" donnent le nom de ce qu'il y a dans le grab), et qui sera relié par un arc de graphe au site qu'il faut.
Pour résumer, notre graphe aura des sites reliés entre eux (comme d'hab) mais aussi, autour de chaque site, plein de noeuds de type "Image" qui seront les images contenues dans ces sites.
Un peu de DOM
Téléchargez le DOM Inspector et installez-le si vous ne l'avez pas déjà, et allez-voir sur cette page si j'y suis : https://developer.mozilla.org/en/Introduction_to_DOM_Inspector
C'était un piège, je n'y étais pas ! Mais vous avez maintenant vu l'essentiel de ce qu'est le DOM. Vous avez vu l'arborescence (à gauche) et comment naviguer dedans. C'est typiquement ce qui vous servira à écrire le target_node_template.
Il ne manque qu'un détail. Vous avez bien noté le petit popup "DOM Inspector Information Types", qui est juste avant le titre de la fenêtre de droite ? En ce qui nous concerne, il faut toujours le laisser sur "Javascript Object", la dernière option. Ca marche comme ça :
Quand vous cliquez sur un noeud DOM dans l'arborescence à gauche, à droite vous aurez la liste de ses attributs. Attention, les attributs changent selon la nature des noeuds. C'est pour ça qu'il faut toujours vérifier. Par exemple, les noeuds image, c'est-à-dire dont le nodeName est "IMG", ont un attribut "src" qui contient l'URL de l'image. Mais les noeud de nodeName "P" (les paragraphes) n'ont pas d'attribut "src". De même les balises "A" ont un attribut "href" etc. Il faut bien regarder ces attributs pour savoir quoi écrire dans le grab, et aussi dans le target_node_template.
Les types de define_element
Type : independantEntity
Ah ! Ce type est réellement le type le plus simple. Il récupère une information, et en fait un noeud dans le graphe. Par exemple, récupérer des vidéos YouTube comme autant de noeuds dans un graphe (on verra les liens plus loin avec un autre type). Voici un exemple :
<define_element label="MySpaceProfile" type="independantEntity"> <target_node_template>theNode.nodeName=="SPAN" && theNode.className=="nametext"</target_node_template> <grab>theNode.textContent</grab> </define_element>
Comme pour node, il n'y a qu'un target_node_template et un grab. Ici il s'agit de récupérer un profil MySpace. Le target_node_template signifie "cibler tout noeud de nom 'SPAN' et de classe 'nametext' " et le grab signifie 'récupère le contenu textuel du noeud'.
Voici quelques précisions pour comprendre à fond cet exemple :
- D'abord, le "et". Le "et" qui veut dire "si ceci ET cela", le "et" qu'on utilise dans le target_node_template, s'écrit normalement comme ça : "&&" (double esperluette). Mais n'oublions pas que nous sommes en XML... Il faut donc déspécifier ! Il y a peu de caractères à déspécifier. Les inférieur à "<" et supérieur à ">", le point virgule ";" et l'esperluette ou "et ampère". En XML on ne déspécifie pas avec l'antislash, il y a un code pour chaque caractère spécial. Ici c'est celui-là : "&". Donc notre double esperluette devient : "&&", ce qui veut dire "ET".
- Ensuite, le SPAN : les balises SPAN sont des "blocs horizontaux". Il y a un autre type de bloc très important, le "DIV". Quand on regarde les DOM on trouve beaucoup de DIV et quelques SPAN. Bref, le SPAN est une balise html qui contient du texte (notamment).
- Le className, c'est-à-dire la classe, c'est un attribut spécial que le webmaster rajoute à un élément, et qui permet de gérer plus facilement l'interface. Dans les feuilles de style CSS, on pourra dire "tout ce qui est de classe 'blabla' sera en rouge sur fond bleu". Bref, ici on a besoin de traquer les SPAN, mais en plus on ne veut que ceux d'une classe bien particulière. En effet il y a d'autres SPAN ailleurs dans le DOM qui contiennent des trucs qu'on ne veut pas.
- Le textContent est un attribut excellent, fabriqué par Firefox, et qui donne le texte qu'il y a "dans" l'élément, même s'il y a une sous-structure compliquée. Autrement dit, même si le texte est dans des sous-sous-boîtes diverses et variées, on peut toujours demander le textContent et on aura tout ce texte.
Type : entityUniqueProperty
Commençons par un exemple :
<define_element label="MySpaceProfile" type="independantEntity">
<target_node_template>
theNode.nodeName=="SPAN"
&& theNode.className=="nametext"
</target_node_template>
<grab>theNode.textContent</grab>
</define_element>
<define_element label="URL" type="entityUniqueProperty">
<target_node_template>
theNode.nodeName=="DIV"
&& theNode.firstChild.nodeValue.substr(2,23)=="http://www.myspace.com/"
</target_node_template>
<grab>theNode.firstChild.nodeValue.substr(2, theNode.firstChild.nodeValue.length-4)</grab>
<entityType>MySpaceProfile</entityType>
</define_element>Décortiquons un peu :
- Il y a deux define_element (deux choses qu'on récupère).
- Le premier define_element est de type "independantEntity" : on a déjà vu ça au dessus. En fait, c'est exactement le même exemple.
- Le deuxième define_element est du nouveau type que l'on étudie, le entityUniqueProperty
- Son label est "URL" : vous avez compris que nous sommes en train de récupérer l'URL du profil MySpace !
- Remarquez aussi qu'il y a un nouveau sous-noeud XML du define_element : il y a un "entityType" en plus du target_node_template et du grab habituels.
Voici comment fonctionne le entityUniqueProperty :
- Ce filtre cible un noeud (target_node_template, comme d'hab)
- Il récupère un contenu textuel (le grab, comme d'hab)
- Il considère ce contenu comme la valeur d'une propriété d'une entité
- L'entité est spécifiée dans le entityType : il prend la dernière entité récupérée qui soit du type spécifié dans le entityType
- Le nom de la propriété est celui spécifié dans l'attribut "label" du define_element
- Il y a une et une seule valeur pour la propriété donnée d'un noeud donné, autrement dit si l'entité avait déjà une valeur pour la propriété, elle est remplacée par le nouvelle.
Pour résumer :
Le filtre "entityUniqueProperty" attribue la valeur "grab" tirée du "target_node_template" à la propriété "label" du dernier noeud récupéré de type "entityType".
Dans notre cas, le filtre récupère le contenu comme d'hab et l'attribue à la propriété "URL" de l'entité "MySpaceProfile" récupérée juste avant.
Dans une heuristique, on trouvera donc souvent un define_element "independantEntity" suivi de plusieurs "entityUniqueProperty" qui définiront ses propriétés.
Puisqu'on en est là, penchons-nous un peu sur ces heuristiques DOM un peu plus compliquées. D'abord, le target_node_template :
theNode.nodeName=="DIV" && theNode.firstChild.nodeValue.substr(2,23)=="http://www.myspace.com/"
- La première ligne dit "on cible un noeud dont le nom est DIV". C'est un bloc comme le SPAN vu plus haut, mais encore plus générique. Dans un DOM on trouvera la plupart du temps de nombreuses DIV imbriquées les unes dans les autres.
- La seconde ligne dit "ET ce noeud doit être tel que son premier fils doit avoir une valeur V, sachant que V est une chaîne de caractères contenant, entre le caractère 2 et le caractère 23, la chaîne 'http://www.myspace.com'."
Regardons le grab :
theNode.firstChild.nodeValue.substr(2, theNode.firstChild.nodeValue.length-4)
- Cette ligne ressemble à la deuxième du target_node_template
- On a toujours le "firstChild" et le "substr", mais ce qu'il y a dans le "substr" est plus compliqué.
Pas trivial. Pour mieux comprendre, prenons le problème à l'envers... le DOM se présente ainsi :
- DIV
- textNode
Petite explication. D'abord, on a un textNode dans un DIV. Pour accéder aux enfants d'un noeud, on peut aller chercher dans la liste des enfants : childNodes[n] donne le n-ième enfant. Mais on peut aussi faire "firstChild" : theNode.firstChild donne le premier enfant ! Donc si "theNode" est le DIV, "theNode.firstChild" est le textNode.
Le DIV est un container classique. Le textNode est en revanche quelque chose d'assez particulier ! En fait dans un DOM, on trouve deux sortes de noeuds DOM. D'une part les noeuds-éléments-DOM (tous ceux qu'on a vus auparavant, comme le SPAN, le A ou le DIV) et les noeuds-texte-DOM, autrement dit les textNode. La grosse différence c'est que les textNode ne contiennent que du texte, et n'ont pas d'enfant. Il y a des attributs qui marchent partout, mais pas sur les textNodes. En ce qui nous concerne, nous retiendrons juste que pour avoir le texte d'un textNode, il suffit d'accéder à sa valeur :
theNode.firstChild.nodeValue
Dans notre cas, la valeur du textNode est particulière, parce qu'il y a des espaces en trop. Pour qu'on les voie bien, on va les remplacer par des underscore "_". Voici la valeur que l'on obtient :
"__http://www.myspace.com/thedoband____"
On veut bien évidemment virer ces espaces qui sont là à chaque fois. Pour ce faire, on a la fonction "substr", ce qui veut dire "substring" ou autrement "sous chaîne de caractères". On dit à la fonction de où à où il faut couper, et on obtient juste ce qu'il faut.
Où faut-il couper ? Dans notre cas, il faut enlever les deux premiers caractères, on commence donc à 2. Et on veut enlever aussi les quatre derniers caractères. On finit donc à 4 de la fin, c'est à dire à L-4 si "L" est la longueur de la chaîne. Comment avoir la longueur de la chaîne ? Comme ça :
theNode.firstChild.nodeValue.length
Donc la sous-chaîne, sans les espaces, se récupère comme ça :
theNode.firstChild.nodeValue.substr(2, theNode.firstChild.nodeValue.length-4)
Et là on obtient bien ce qu'on voulait :
"http://www.myspace.com/thedoband"
Donc après ces explications, vous devriez tout-à-fait saisir le sens de la deuxième ligne du target_node_template :
theNode.firstChild.nodeValue.substr(2,23)=="http://www.myspace.com/"
Elle signifie que l'on recherche une noeud (le DIV) dont le premier fils (le textNode) est un texte commençant par "http://www.myspace.com/" (si l'on oublie les espaces parasites).
Une dernière remarque sur cet exemple. Est-ce que cette heuristique n'est pas bizarre ? La plupart des heuristiques sont "bizarres" et il faut parfois un peu se creuser la tête pour les faire, mais on y arrive. Mais c'est vrai que celle-ci a l'air assez "large" :
- on cherche un DIV
- qui contient un textNode de valeur "http://www.myspace.com/" (hors espaces parasites).
La question est donc : pourquoi est-ce qu'il y aurait une seule occurrence d'une telle structure, sachant que myspace est le nom de la plateforme, et que les DIV comme les textNode pullulent dans tout le web !? Deux réponses à ça :
- On teste : ça marche. Une seule occurrence. L'empirisme l'emporte !
- Il y a une autre raison... Ce qui est spécial, c'est que l'URL du profil MySpace n'est pas dans un lien. Certes, c'est une URL, mais on ne peut pas cliquer dessus. Si on pouvait, alors le textNode serait dans un A qui serait dans la DIV. Mais il n'y a pas d'élément A, il n'y a pas de lien. Et sur le web, c'est assez rare d'avoir une URL non-cliquable. Alors pourquoi ? Parce que le lien est le lien de la page... On pourrait le récupérer autrement, mais cette méthode marche. Dans un profil MySpace il y a l'adresse du profil inscrite, et elle n'est pas cliquable car la page renvoie à elle-même ! C'est pourquoi cette simple structure permet de récupérer l'adresse du profil très simplement.
Voyons maintenant un autre exemple un peu différent. Celui-ci s'intéresse à Wikipedia :
<define_element label="WikipediaArticle" type="independantEntity">
<target_node_template>theNode.nodeName=="HTML"</target_node_template>
<grab>theNode.baseURI.split("/wiki/")[1].split("#")[0]</grab>
</define_element>
<define_element label="URL" type="entityUniqueProperty">
<target_node_template>theNode.nodeName=="HTML"</target_node_template>
<grab>theNode.baseURI.split("#")[0]</grab>
<entityType>WikipediaArticle</entityType>
<entity>theNode.baseURI.split("/wiki/")[1].split("#")[0]</entity>
</define_element>Décortiquons :
- On a comme ci dessus un independantEntity suivi d'un entityUniqueProperty. C'est classique.
- Dans le entityUniqueProperty on a non-seulement le entityType qu'on a déjà vu, mais aussi un entity.
- Remarque importante : cette ligne du entity est la même que le grab du independantEntity, et les target_node_template sont les mêmes.
A ce stade vous devriez comprendre où je veux en venir. Avec cette syntaxe un peu plus complète, on spécifie à quelle entité s'applique la propriété qu'on récupère. Donc on ne s'intéresse pas à la dernière entité du type spécifié, mais à celle qu'on désigne. C'est pour ça que la ligne entity est la même que le grab du independantEntity, et que les deux target_node_template sont identiques. Pour résumer voilà ce qui se passe dans le filtre :
- Entité : va au noeud A et récupère B
- Propriété : va au noeud A et récupère la propriété C que tu appliques à B
Revenons un peu sur les instructions DOM. Il y en a deux que nous n'avons pas vues :
<grab>theNode.baseURI.split("/wiki/")[1].split("#")[0]</grab>
...
<grab>theNode.baseURI.split("#")[0]</grab>Commençons par la deuxième, elle est plus simple. Commençons avec la base :
theNode.baseURI
donne :
"http://fr.wikipedia.org/wiki/Banane#Description"
Remarque : le noeud qu'on traque est de nodeName "HTML" : il n'y en a toujours qu'un seul, et c'est le document entier. Son attribut "baseURI" donne l'URL de la page. L'URL est donc celle ci-dessus.
Cette URL est celle de la page Banane sur Wikipedia. Mais en fait, pas tout-à-fait. La bonne URL est celle-ci :
"http://fr.wikipedia.org/wiki/Banane"
Elle n'a pas le dièse et de "Description". Ce rajout signifie qu'il y a un lien interne, qu'il faut aller au paragraphe "Description" dans la page. On veut donc l'enlever. Ce qu'on fait alors, c'est qu'on sépare l'URL en deux autour du dièse, et qu'on récupère la partie gauche. On appelle ça "splitter" :
theNode.baseURI.split("#")Quand on fait ça on récupère un array (une liste). On veut donc prendre le premier élément. Vu que quand on compte on commence de zéro, ça donne ça :
theNode.baseURI.split("#")[0]Et on récupère ce qu'on voulait :
"http://fr.wikipedia.org/wiki/Banane"
Même principe pour l'autre expression, sauf qu'on splitte deux fois :
theNode.baseURI.split("/wiki/")[1].split("#")[0]Si vous avez suivi, vous aurez compris qu'on obtient ceci :
"Banane"
On a le nom. C'est gagné !
Type : entityToEntityLink
Type : entityToSiteLink
Type : siteToEntityLink
Type : siteToSiteLink
Type : siteUniqueProperty
Type : node
Type : crawlPath
- Vous devez vous identifier ou créer un compte pour écrire des commentaires

