Posts Tagged ‘Wikipedia’

WikiDump: Tool to get Wikipedia content

Wednesday, June 30th, 2010

WikiDump tool has been made to get Wikipedia content from a list of article ids. The tool connect on http://en.wikipedia.org to get text data.

For more informations about getting Wikipedia content, you can see this post.

Here is the source code:


package com.devbypractice;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class WikiDump
{
 /**
 * Get xhmlt content of Wikipedia source to String
 *
 * @param sUrlSource Url Source
 * @param sEncoding Encoding
 * @return xhtml content
 * @throws Exception
 */
 public static String dumpWikiXhtmlToString(String sUrlSource, String sEncoding)
 throws Exception
 {
 String inputLine;

 URL url;
 url = new URL(sUrlSource);
 URLConnection urlConn = url.openConnection();

 // TimeOut 30sec
 //
 urlConn.setConnectTimeout(30000);
 BufferedReader in = new BufferedReader(new InputStreamReader(
 urlConn.getInputStream(), sEncoding));

 StringBuilder sb = new StringBuilder();

 // XML header
 //
 sb.append("<?xml version=\"1.0\"?>");
 sb.append("<WikiContent>");

 // Copy response to buffer
 //
 while ((inputLine = in.readLine()) != null) {
 sb.append(inputLine);
 sb.append("\n");
 }
 in.close();

 // XML footer
 //
 sb.append("</WikiContent>");

 return sb.toString();
 }

 /**
 * Load article ids from files File must have an id on each line.
 *
 * @param sFileName
 * @return Articles ids
 * @throws Exception
 */
 public static List<Integer> loadArticleIdsFromFile(String sFileName)
 throws Exception
 {
 List<Integer> articleIds = new ArrayList<Integer>();

 FileReader reader = new FileReader(sFileName);
 BufferedReader buffReader = new BufferedReader(reader);

 String sLine = "";

 while ((sLine = buffReader.readLine()) != null) {
 Integer id = Integer.parseInt(sLine);
 articleIds.add(id);
 }

 buffReader.close();

 return articleIds;

 }

 /**
 * Display usage
 */
 public static void displayUsage()
 {
 System.out.println("Arguments:");
 System.out.println("<Article id source file path> <Output directory>");
 System.out.println("");
 System.out.println("Notes:");
 System.out.println("Article id file must have an id on each line. ");
 System.out.println("Output directory must exist.");

 }

 /**
 * Text cleanup. References [12], [3] etc... are deleted.
 *
 * @param text Text to clean
 * @return Cleaned text
 */
 public static String cleanup(String text)
 {
 return text.replaceAll("\\[[0-9]+\\]", "");
 }

 /**
 * Main entry
 *
 * @param args Arguments
 */
 public static void main(String[] args)
 {
 try {

 // UTF-16 chars count for output files
 // 1000000 ~= 2 Mo
 //
 final int fileSizeLimit = 1000000;

 // Current output file size
 // set to MAX + 1 for the file to be created at first time
 //
 int currentFileSize = fileSizeLimit + 1;

 // Url to get online Wikipedia article by id
 //
 String sWikiUrlFormat = "http://en.wikipedia.org/w/index.php?action=render&curid=%d";

 FileOutputStream outStream = null;
 OutputStreamWriter writer = null;

 if (args.length != 2) {
 displayUsage();

 }
 else {

 // Get article ids from file
 //
 List<Integer> articleIds = loadArticleIdsFromFile(args[0]);

 // Output dir
 //
 String sDestinationDir = args[1];

 // For each article id
 //
 for (Integer articleId : articleIds) {

 try {

 if (currentFileSize > fileSizeLimit) {
 if (writer != null) {
 writer.close();
 }
 outStream = new FileOutputStream(sDestinationDir + "\\"
 + articleId + ".txt");
 writer = new OutputStreamWriter(outStream, "UTF-16");

 currentFileSize = 0;

 }

 // Build article url
 //
 String sWikiUrl = String.format(sWikiUrlFormat, articleId);

 // Get article content
 //
 String sWiki = dumpWikiXhtmlToString(sWikiUrl, "UTF-8");

 // Load Xhtml content
 //
 InputSource is = new InputSource(new StringReader(sWiki));
 Document doc = DocumentBuilderFactory.newInstance()
 .newDocumentBuilder()
 .parse(is);

 // writer.write("========= " + articleId + " ==========\n");

 // Target desired wikipedia content
 //
 NodeList nl = XPathProcessor.getInstance().EvaluateMutli(
 "/WikiContent/p", doc);

 // For each node
 //
 for (int i = 0; i < nl.getLength(); i++) {
 String sContent = "";
 try {
 Node node = nl.item(i);

 // get text content
 //
 sContent = node.getTextContent();

 // clean text
 //
 sContent = cleanup(sContent);

 // Write in output file
 //
 writer.write(sContent);
 writer.write("\n\n");
 writer.flush();

 // Increase output file size count
 //
 currentFileSize += sContent.length() + 2;
 }
 catch (Exception e2) {
 String sLog = "Paragraph not get " + i
 + " for id=" + articleId;
 System.out.println(sLog);
 }
 }
 }
 catch (Exception e3) {
 String sLog = "Article not get for id = " + articleId
 + "\n";
 System.out.println(sLog);

 e3.printStackTrace();
 }

 }
 writer.close();
 }
 }

 catch (Exception e) {
 e.printStackTrace();

 }

 }

}

To compile the source you will need a XPath query manager available on this link.

A compiled version of WikiDump can be downloaded here.

Get Wikipedia content

Tuesday, June 29th, 2010

Wikipedia is a huge source of content for people who need big text data.

Wikipedia allow to get these data and provide database and documentation on this link.

My own experience allow to make such statements:

  • It’s painfull to parse Wiki language (Wikecode or WikiText), even if many tools exist (Mylyn etc..), proprietary templates make substitutions not possible.
  • The best Wikitext parser is Wikipedia itself…
  • Creating a local Wikipedia mirror is painfull and poorly documented.
  • Get parsed arcticles from Wikipedia website is not a problem when it is as slow as 1 article per second, but getting a large amout of articles is very long (1 article = 1 sec <=> 100000 articles = 1 whole day)
  • Arcticles must be filtered because everything is in Wikipedia, best and worst.

I was able to get data from Wikipedia in these few steps:

  1. Prerequisites
    MySQL server and tools
    Java virtual machine (the path of java.exe must be in PATH environnement variable)
    Custom Java tool WikiDump [ Sources - Compiled version ] (in the compiled version a batch is provided to launch a demo,  an exception is thrown as expected but it doesn’t interrupt  the process )
  2. Go to website http://download.wikimedia.org/enwiki/latest/ to get MySQL dumps:
    enwiki-latest-category.sql.gz
    : Contains a list of categories and arcticles count.
    enwiki-latest-categorylinks.sql.gz
    : Makes the link between articles and categories.
  3. Import theses scripts in a MySQL database (with Windows I used MySQL Administrator).
    Note: The process is long (many hours).
  4. Filter wanted articles by executing the following query to get desired portals:SELECT * FROM category WHERE cat_pages > 0 AND cat_title LIKE “Portal:%” ORDER BY cat_pages DESCOnce portals choosen  (take and refuse), select included articles. Execute following query (put your own portals here):

    SELECT cl_data_in.cl_from FROM ((SELECT DISTINCT cl3.cl_from FROM categorylinks AS cl3 WHERE cl3.cl_to IN (“Portal:take1,Portal:take2″)) AS cl_data_in)
    WHERE cl_data_in.cl_from NOT IN (SELECT DISTINCT cl2.cl_from FROM categorylinks AS cl2 WHERE LOWER(cl2.cl_to LIKE “liste d%”) OR cl2.cl_to IN (“Disambiguation_pages”,”Portal:refuse1″))

    Once article list is filtered, export result in csv file, not matter file format, the goal is to have an article id by line.

    Cleanup the file, delete column name and empty lines.

  5. To launch process to get content, type in command line:
    java -jar WikiDump.jar article_file_path output_directory > log.txt

WikiDump : Outil de récupération du contenu de Wikipedia

Sunday, June 27th, 2010

L’utilitaire WikiDump permet de récupérer le contenu de Wikipedia à partir d’une liste d’identifiant d’articles. L’outil se connecte sur le site fr.wikipedia.org afin de récupérer des données à jour et correctement formattées.

Pour plus de détail concernant la récupération des données de Wikipedia rendez vous sur cet article.

Pour la curiosité ou bien pour modifier son comportement voici le code source:


package com.devbypractice;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * Classe contenant un main permettant de faire un dump
 * de wikipédia.
 *
 */
public class WikiDump
{
 /**
 * Récupère le contenu xhtml d'une url source Wikipédia en String
 *
 * @param sUrlSource Url Source
 * @param sEncoding Encodage
 * @return Le contenu du html
 * @throws Exception
 */
 public static String dumpWikiXhtmlToString(String sUrlSource, String sEncoding) throws Exception {
 String inputLine;

 URL url;
 url = new URL(sUrlSource);
 URLConnection urlConn = url.openConnection();

 // TimeOut de 30sec
 //
 urlConn.setConnectTimeout(30000);
 BufferedReader in = new BufferedReader(
 new InputStreamReader(
 urlConn.getInputStream(), sEncoding));

 StringBuilder sb = new StringBuilder();

 // En tête XML
 //
 sb.append("<?xml version=\"1.0\"?>");
 sb.append("<WikiContent>");

 // Copie du contenu html vers le buffer
 //
 while ((inputLine = in.readLine()) != null){
 sb.append(inputLine);
 sb.append("\n");
 }
 in.close();

 // Fin XML
 //
 sb.append("</WikiContent>");

 return sb.toString();
 }

 /**
 * Charge la liste des identifiants des articles d'un fichier
 * vers un liste d'entiers.
 * Le fichier doit contenir un id sur chaque ligne.
 *
 * @param sFileName
 * @return Liste des identifiants des articles
 * @throws Exception
 */
 public static List<Integer> loadArticleIdsFromFile(String sFileName) throws Exception
 {
 List<Integer> articleIds = new ArrayList<Integer>();

 FileReader reader = new FileReader(sFileName);
 BufferedReader buffReader = new BufferedReader(reader);

 String sLine ="";

 while ((sLine = buffReader.readLine())!=null) {
 Integer id = Integer.parseInt(sLine);
 articleIds.add(id);
 }

 buffReader.close();

 return articleIds;

 }

 /**
 * Affiche le mode d'emploi de la ligne de commande
 */
 public static void displayUsage() {
 System.out.println("Arguments:");
 System.out.println("<Chemin fichier source id articles> <Chemin de destination>");
 System.out.println("");
 System.out.println("Notes:");
 System.out.println("Le fichier source des id articles doit être un fichier " +
 "comportant la liste des identifiants articles Wikipédia à récupérer, " +
 "ces identifiants doivent être séparés par une fin de ligne");
 System.out.println("Le chemin de destination doit exister.");

 }

 /**
 * Nettoyage du texte.
 * Les références du type [12], [3] etc... sont supprimées.
 *
 * @param text Texte à nettoyer
 * @return Texte nettoyé
 */
 public static String cleanup(String text) {
 return text.replaceAll("\\[[0-9]+\\]", "");
 }

 /**
 * Entrée principale
 *
 * @param args Arguments
 */
 public static void main(String[] args) {
 try {

 // Taille en caractères UTF-16 des fichiers de sortie
 // 1 Millions ~= 2 Mo
 //
 final int fileSizeLimit = 1000000;

 // Taille courante du fichier de sortie
 // mise à MAX + 1 afin qu'il soit créé lors du premier passage
 //
 int currentFileSize = fileSizeLimit + 1;

 // Url de récupération des articles Wikipédia par leur identifant article
 //
 String sWikiUrlFormat = "http://fr.wikipedia.org/w/index.php?action=render&curid=%d";

 FileOutputStream outStream = null;
 OutputStreamWriter writer = null;

 if (args.length != 2) {
 displayUsage();

 }
 else {

 // Récupération de la liste des identifiants articles à partir du fichier
 //
 List<Integer> articleIds = loadArticleIdsFromFile(args[0]);

 // Format de destination des fichiers du contenu de wikipédia
 //
 String sDestinationDir = args[1];

 // Pour chaque identifiant d'article
 //
 for (Integer articleId : articleIds) {

 try {

 // Si la taille courant du fichier de sortie est supérieure
 // à la taille max on en crée un nouveau
 //
 if (currentFileSize > fileSizeLimit) {
 if (writer != null) {
 writer.close();
 }
 outStream = new FileOutputStream(sDestinationDir + "\\" + articleId + ".txt");
 writer = new OutputStreamWriter(outStream, "UTF-16");

 currentFileSize = 0;

 }

 // Construction de l'url de l'article
 //
 String sWikiUrl = String.format(sWikiUrlFormat, articleId);

 // Récupération du contenu de l'article
 //
 String sWiki = dumpWikiXhtmlToString(sWikiUrl, "UTF-8");

 // Chargement XML du contenu
 //
 InputSource is = new InputSource(new StringReader(sWiki));
 Document doc = DocumentBuilderFactory.newInstance()
 .newDocumentBuilder()
 .parse(is);

 //writer.write("========= " + articleId + " ==========\n");

 // Ciblage du contenu à récupérer via XPath
 //
 NodeList nl = XPathProcessor.getInstance().EvaluateMutli(
 "/WikiContent/p", doc);

 // Pour chaque noeud
 //
 for (int i = 0; i < nl.getLength(); i++) {
 String sContent = "";
 try {
 Node node = nl.item(i);

 // Récupération du texte
 //
 sContent = node.getTextContent();

 // Nettoyage
 //
 sContent = cleanup(sContent);

 // Ecriture dans le fichier
 //
 writer.write(sContent);
 writer.write("\n\n");
 writer.flush();

 // Augmentation de la taille du fichier courant
 //
 currentFileSize += sContent.length() + 2;
 }
 catch (Exception e2) {
 String sLog = "Impossible de récupérer le paragraphe " + i + " pour id=" + articleId;
 System.out.println(sLog);

 }
 }
 }
 catch (Exception e3) {
 String sLog = "Impossible de récupérer l'article id=" + articleId + "\n";
 System.out.println(sLog);

 e3.printStackTrace();
 }

 }
 writer.close();
 }
 }

 catch (Exception e) {
 e.printStackTrace();

 }

 }

}

Cet utilitaire nécessite une classe de gestion des requêtes XPath, dont le code est à cette adresse.

Une version déjà compilée de WikiDump est disponible à cette adresse.

Récupérer le contenu de Wikipedia

Saturday, June 26th, 2010

Wikipedia est une gigantesque source de contenu pour celui qui a besoin d’ une grand quantité de données en format texte.

Récupérer ces données est autorisé, Wikipedia fourni même des moyens pour récupérer tous styles de données ainsi que de la documentation (sommaire et fouillie) à cette adresse.

Mon expérience personnelle permet de faire les constats suivants:

  • Il est difficile de parser le balisage propre à Wiki (Wikicode ou Wikitext), même si de nombreux outils existent en Java ( Mylyn etc..) la présence de templates spécifiques Wikipedia rend certaines substitutions impossibles simplement.
  • Le meilleur parser de Wikitext est Wikipedia lui même…
  • Installer un mirroir exact de Wikipedia en local est long fastidieux et assez mal documenté
  • La récupération des articles parsés directement par Wikipedia à 1 article par seconde ne pose pas de problème à Wikipedia, mais le processus est très long (1 article = 1 sec <=> 100000 articles = 1 jour)
  • Les articles doivent être filtrés car on a de tout, le meilleurs comme le pire

J’ai pu récupérer les données de Wikipedia de la manière suivante:

  1. Pré requis
    Serveur et outils MySQL
    Machine virtuelle java (le chemin de java.exe doit être dans la variable d’environnement PATH)
    L’utilitaire maison java WikiDump [ Sources - Version compilée ] (dans la version compilée un batch est fourni pour lancer une démo, l’exception qui apparait est voulue, elle n’interrompt pas le traitement )
  2. Se rendre sur le site http://dumps.wikimedia.org/frwiki/latest/ afin de télécharger les dumps MySQL:
    frwiki-latest-category.sql.gz
    : Contenant la liste des catégories et le nombre d’articles qui s’y ratache.
    frwiki-latest-categorylinks.sql.gz
    : Permettant de faire le lien entre les articles et les catégories.
  3. Importer les scripts dans une base de données MySQL (sous Windows j’ai utilisé MySQL Administrator).
    Note: Le processus est relativement long (3 heures environ).
  4. Filtrer les articles voulus en lançant la requête suivante afin de récupérer les catégories Portail : SELECT * FROM category WHERE cat_pages > 0 AND cat_title LIKE “Portail:%” ORDER BY cat_pages DESC

    Une fois les portails choisis (A prendre et A refuser), il est nécessaire d’en sélectionner les articles. Lancer la requête suivante en mettant votre propre liste de portails:

    SELECT cl_data_in.cl_from FROM ((SELECT DISTINCT cl3.cl_from FROM categorylinks AS cl3 WHERE cl3.cl_to IN ("Portail:APrendre1,Portail:APrendre2")) AS cl_data_in)
    WHERE cl_data_in.cl_from NOT IN (SELECT DISTINCT cl2.cl_from FROM categorylinks AS cl2 WHERE LOWER(cl2.cl_to LIKE "liste d%") OR cl2.cl_to IN ("Homonymie","Portail:ARefuser1"))

    Une fois la liste des articles récupérés les exporter dans un fichier csv ou autre, le format importe peu, l’essentiel est d’avoir un identifiant par ligne.

    Nettoyer le fichier en supprimant le nom de colonne et les ligne vides.

  5. Pour lancer la récupération du contenu, dans la ligne de commande, tapper:java -jar WikiDump.jar Chemin_fichier_articles Repertoire_de_destination > log.txt

Une fois le dernier (long) traitement effectué, vous avez pu récupérer une quantité de données au delà de vos espérances.

Par courtoisie, si vous effectuez un gros ramassage, n’oubliez pas de faire à un don à Wikipedia.