Recevez les mises à jour gratuites du blog par Email : »» Garanti sans spam indésirable ««

Récupérer le détail d’une carte Magic the Gathering avec un parseur HTML

de Mimie le 12 février 2010

Rubrique : Programmation

Présentation

Je travaille depuis peu sur un site web personnel concernant Magic the Gathering, mon jeu de cartes préféré qui passe bien devant le poker, la belote, la manille et le tarot (voir les articles de Greg ici et  pour voir de quoi ça parle).

Le site devant proposer aux utilisateurs de visionner les cartes, il est primordial d’avoir à porter de main toutes les informations concernant les différentes cartes qui existent, comment s’y prend t’on ?

Je vois trois façons de faire :

  1. Vous utilisez la base de données déjà alimentées d’un ami ou d’un site marchand : chose qui n’arrivera jamais, de plus il faudrait alors se plier à leur modèle de données qui ne vous conviendra pas forcément
  2. Vous appelez un service web qui depuis un nom de carte ou un identifiant vous renvoie toutes les informations nécessaires concernant cette carte (sous forme JSON ou XML) : un tel service n’existe pas ou je suis passé à côté
  3. Vous alimentez vous-même votre base de données en récupérant chaque information de chaque carte par le biais d’un parseur HTML : difficile à mettre en place, tributaire du code source du site cible, mais l’énorme avantage est que vous définissez vous-même le modèle de données et qu’au final vous obtenez votre propre base de données

Jericho

Voici le nom du parseur HTML que j’utilise, Jericho, il ne m’avait servi pour le moment qu’à modifier les éléments d’un source HTML, plus précisément modifier le contenu HTML généré par une Taglib, voir article précédent.

Nous allons à présent nous en servir pour l’extraction de données à partir d’un document HTML.

Voici les différentes étapes (ou algorithme) pour la récupération des données d’une carte, nous utiliserons comme référant le site officiel de la base de données des cartes Magic, Gatherer :

  1. Récupérer l’url de votre carte détaillée à récupérer, ex : http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=192230
  2. Récupérer le source HTML de cette page qui contient toutes les informations qu’il vous faut
  3. // net.htmlparser.jericho.Source
    Source source = new Source(new URL("url de votre carte"));
    
  4. A partir de cet objet « source » tout est à présent possible, il faudra extraire une à une les informations souhaitées : nom de la carte, coût en mana, force et endurance, type de carte, image, édition, etc.

Données à extraire

Je ne vais pas donner le code pour la récupération de chaque détail de la carte mais juste quelques uns pour vous montrer le principe :

  • Nom
  • Element cardNameElement = source.getElementById("ctl00_ctl00_ctl00_MainContent_SubContent_SubContentHeader_subtitleDisplay");
    String name = "";
    if (cardNameElement != null) {
    	name = cardNameElement.getContent().toString().trim();
    }
    System.out.println("name : " + name);
    
  • Image
  • Pour récupérer l’image sous forme d’un tableau de bytes afin de pouvoir l’enregistrer en base :

    String imageUrl = "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=192230";
    byte[] imageBytes = null;
    try {
    	BufferedImage imageBuffered = ImageIO.read(new URL(imageUrl));
    	ByteArrayOutputStream out = new ByteArrayOutputStream();
    	ImageIO.write(imageBuffered, "jpg", out);
    	imageBytes = out.toByteArray();
    } catch (Exception e) {
    	System.out.println(e.getMessage());
    }
    System.out.println("imageBytes : " + imageBytes);
    

    Pour sauvegarder l’image sur votre disque dur :

    String imageUrl = "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=192230";
    try {
    	BufferedImage imageBuffered = ImageIO.read(new URL(imageUrl));
    	FileOutputStream out = new FileOutputStream(new File("votre_répertoire_images" + "nom_de_la_carte" + ".jpg"));
    	ImageIO.write(imageBuffered, "jpg", out);
    } catch (Exception e) {
    	System.out.println(e.getMessage());
    }
    
  • Type et sous-type
  • Les cartes de créatures sont typées « Créature » mais ont aussi un sous-type comme par exemple « Vampire », nous séparons ici ces deux informations. A noter aussi que si le numéro de la carte recherchée permet d’obtenir les informations en français de la carte alors le source du site diffère, d’où quelques ajustations permettant de rendre le code générique :

    Element typesElement = source.getElementById("ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_typeRow");
    String type = "";
    String subtypes = "";
    Integer globalType = null;
    if (typesElement != null) {
    	Element typesValueElement = typesElement.getAllElements(HTMLElementName.DIV).get(2);
    	String types = typesValueElement.getContent().toString().trim();
    	String separator = language.equals(ConstantsParsing.ENGLISH_LANGUAGE) ? "—" : ":";
    	types.replaceAll(Pattern.quote("?"), Matcher.quoteReplacement(separator)); // bad character encoding
    	String[] splitTypes = types.split(separator);
    	if (splitTypes.length == 1) {
    		type = splitTypes[0].trim();
    	} else {
    		type = splitTypes[0].trim();
    		subtypes = splitTypes[1].replaceAll("-", "").trim();
    	}
    }
    System.out.println("type : " + type);
    System.out.println("subtypes : " + subtypes);
    
  • Nom de l’artiste
  • Element artistElement = source.getElementById("ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_artistRow");
    String artist = "";
    if (artistElement != null) {
    	Element artistValueElement = artistElement.getAllElements(HTMLElementName.DIV).get(2);
    	Element artistTextElement = artistValueElement.getFirstElement(HTMLElementName.A);
    	artist = artistTextElement.getContent().toString().trim();
    }
    System.out.println("artist : " + artist);
    

Conclusion

Long et fastidieux par le principe même de devoir parser du code HTML, mais Jericho, lui, est très simple d’utilisation et permet assez rapidement d’extraire n’importe quelle données sans avoir à écrire trop de code.

De plus je trouve Jericho très rapide dans la recherche des éléments d’un document ainsi qu’à récupérer le source HTML d’une page à partir de son url.

Je vous le conseille parmi la multitude de parseurs HTML Java à notre portée.

Cet article a été écrit par :

– qui a déjà rédigé 123 posts sur Des Geeks et des lettres.

Passionné d'informatique et développeur JavaEE de métier, je me consacre principalement à écrire des billets sur les sujets du Web et de la programmation Web. Ce blog est un espace qui me permet de partager mes découvertes avec vous et me sert accessoirement de pense bête !

Contacter l'auteur

Jetez aussi un oeil sur :

{ 4 commentaires… à vous de vous exprimer ! }

1 Greg février 12, 2010 à 20 h 34 min

Je pense que cet article intéressera ceux qui apprennent la programmation en effet

Répondre

2 Mimie février 15, 2010 à 14 h 24 min

L’appréhension d’un parseur HTML est destiné à ceux qui maîtrisent déjà le langage HTML (et ses dérivés), sinon il sera impossible d’analyser le code source et donc d’en extraire les données attendues.

Répondre

3 Fabrice mars 14, 2010 à 1 h 13 min

j’ai mis en place cette solution 3 il y a quelques années pour justement un projet SourceForge (firemox) un meta-jeu ( Magic The Gathering entre autre), et le système de picture-provider et data-provider était géré non pas avec un parseur HTML, mais de simple RegExp. J’y vois les avantages suivants :
- la page HTML n’étant pas toujours valide XTML, HTML W3C,… les parseur peuvent échouer.
- les performances!
- le temps de mise en place. Comme tu le souligne, le code est bien trop compliqué avec ce genre de parseur.

Répondre

4 Mimie mars 14, 2010 à 22 h 04 min

Bonjour Fabrice, super initiative le projet firemox :D j’aime beaucoup la façon de représenter une carte Magic sous forme XML.
Je n’ai pas encore récupéré le projet sous mon Eclipse, peux-tu donner un exemple d’expression régulière que tu utilises pour récupérer les informations des cartes ?
Ce projet est toujours suivi ? les nouvelles cartes sont ajoutées au projet régulièrement ?

Répondre

Laissez un Commentaire

Article précédent:

Article suivant: