Subversion Repositories eFlore/Projets.eflore-projets

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php
// Encodage : UTF-8
// +-------------------------------------------------------------------------------------------------------------------+
/**
* Robots
*
* Description : classe permettant d'analyser les pages d'un site web.
* Notes : les noms des pages doivent être dans la bonne casse. http://fr.wikipedia.org/wiki/ambronay ne renvera rien alors que
* http://fr.wikipedia.org/wiki/Ambronay renvera un résultat (Notez le A ou a).
*
//Auteur original :
* @author       Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright    Tela-Botanica 1999-2009
* @link                 http://www.tela-botanica.org/wikini/eflore
* @licence              GPL v3 & CeCILL v2
* @version              $Id: Robot.class.php 2057 2011-05-13 16:39:06Z Jean-Pascal MILCENT $
*/
// +-------------------------------------------------------------------------------------------------------------------+
class Robot extends ScriptCommande {
        /**
         * Indique le nom du Robot.
         */
        private $robot_nom;
        /**
         * Indique le fichier de config de la gestion des cookies du Robot.
         */
        private $cookie;
        /**
         * Indique l'url de départ du Robot.
         */
        private $page;
        /**
         * Tableau des URLs à analyse
         */
        private $pages = array();
        /**
         * Chemin vers un fichier contenant les noms des pages à analyser (un nom de page par ligne).
         */
        private $page_fichier;
        /**
         * Contient soit False soit le chemin vers le dossier où mettre les pages en cache.
         */
        private $cache = false;
        /**
         * Contient le squelette de l'url à utiliser pour récupérer les pages web.
         */
        private $url_tpl = '';
        /**
         * Contient false ou l'encodage des pages d'un site web si celui-ci n'est pas en UTF-8.
         */
        private $encodage = false;
        /**
         * Contient la chaine de caractères indiquant où commencer une recherche d'informations dans la page web.
         */
        private $chaine_debut = '';
        /**
         * Contient la chaine de caractères indiquant où terminer une recherche d'informations dans la page web.
         */
        private $chaine_fin = '';
        /**
         * Tableau des expressions régulières récupérant des données lors de l'analyse
         */
        private $regexps = array();
        /**
         * Indique le dossier ou fichier où le Robot doit sotcker les informations collectées.
         */
        private $sortie;

        public $parametres = array(     '-pgf' => array(false, '', 'Fichier contenant les pages que le Robot doit analyser'),
                                                                '-s' => array(false, '', 'Fichier où stocker les données récupérées par le Robot'),
                                                                '-pg' => array(false, '', 'Nom de la page que le Robot doit analyser'));


        public function executer() {
                $this->page = $this->getParam('pg');
                $this->page_fichier = $this->getParam('pgf');
                $this->cookie = dirname(__FILE__).DS.'configuration'.DS.'cookieconf.txt';
                $this->sortie = $this->getParam('s');

                // Construction du tableau contenant les noms des pages à analyser
                if (empty($this->page_fichier)) {
                        $this->pages[] = $this->page;
                } else {
                        $this->pages = $this->convertirFichierEnTableau($this->page_fichier);
                }

                // Création du chemin du cache si le fichier ini du projet l'indique
                if ($this->getIni('cache_chemin')) {
                        $this->cache = $this->getIni('cache_chemin');
                }

                // Lancement du Robot demandé
                $cmd = $this->getParam('a');
        switch ($cmd) {
                        case 'wp' :
                                $this->lancerRobotWikipedia();
                                break;
                        case 'wp-pays' :
                                $this->lancerRobotWikipediaPays();
                                break;
                        case 'wp-liste-communes' :
                                $this->lancerRobotWikipediaListeCommunes();
                                break;
                        case 'ipni' :
                                $this->lancerRobotIpni();
                                break;
                        case 'cassini' :
                                $this->lancerRobotCassini();
                                break;
                        case 'utm' :
                                $this->lancerRobotUtmConverter();
                                break;
                        default :
                                $this->traiterErreur('Erreur : la commande "%s" n\'existe pas!', array($cmd));
                }
    }

    /**
     * Robot analysant les pages de Wikipedia correspondant à des communes.
     * Exemples d'utilisation :
         * /opt/lampp/bin/php script.php robot -p wp_commune -a wp -pgf ~/importation/robots/eFloreBotWp_INSEE_C.txt -s ~/importation/robots/wp_communes.tsv
         * /opt/lampp/bin/php script.php robot -p wp_commune -a wp -pg Montpellier -s ~/importation/robots/wp_communes.tsv
         * /opt/lampp/bin/php script.php robot -p wp_commune -a wp -pg Montpellier
         *
     * @return unknown_type
     */
    private function lancerRobotWikipedia() {
                // Valeur spécifique de ce Robot
                $this->robot_nom = 'eFloreBotWp';
                $this->url_tpl = 'http://fr.wikipedia.org/wiki/%s';
                $this->regexp_ligne = '/<!-- bodytext -->(.*)<!-- \/bodytext -->/umsi';
                $this->regexps = array( 'CodeInsee' => 'Code commune<\/a><\/th>(?:\n|\r\n)<td>(\d+)<\/td>',
                                                                'Nom' => 'class="entete map" style="[^"]+">(.*)<',
                                                                'Latitutde' => '<span class="geo-dec geo" title=".*"><span class="latitude">(.*)<\/span>',
                                                                'Longitude' => '<span class="geo-dec geo" title=".*">.*<\/span>, <span class="longitude">(.*)<\/span>',
                                                                'Superficie' => 'Superficie<\/a>(?:<\/b><\/td>|<\/th>)(?:\n|\r\n)<td>((?:[0-9]| |&#160;)+(?:,[0-9]+)?) km<sup>2<\/sup>',
                                                                'AltitudeMin' => 'Altitudes<\/a><\/th>(?:\n|\r\n)<td>mini. (-?[0-9]+) m — maxi. [ 0-9]+ m<\/td>',
                                                                'AltitudeMax' => 'Altitudes<\/a><\/th>(?:\n|\r\n)<td>mini. -?[0-9]+ m — maxi. ([ 0-9]+) m<\/td>',
                                                                'Population' => 'Population<\/a><\/th>(?:\n|\r\n)<td>(.*) hab.',
                                                                'PopulationAnnee' => 'Population<\/a><\/th>(?:\n|\r\n)<td>.* hab. <small>\(<a href="\/wiki\/[0-9]+"(?: title="[0-9]+")?>([0-9]+)<\/a>',
                                                                'CodePostal' => 'Code postal<\/a><\/th>(?:\n|\r\n)<td>([0-9]{5}).*<\/td>',
                                                                'PageWikipedia' => '(?:Ce document provient|Récupérée) de « <a href="http:\/\/fr.wikipedia.org\/wiki\/(.*)">'
                                                                );
        // Préparation des noms des pages
            foreach ($this->pages as $id => $nom) {
                $this->pages[$id] = str_replace(' ', '_', $nom);
            }
                $this->analyserUrls();
    }

        /**
     * Robot analysant les pages de Wikipedia correspondant à des pays.
     * Exemples d'utilisation :
         * /opt/lampp/bin/php script.php robot -p wp_commune -a wp-pays -pgf ~/importation/robots/eFloreBotWp_pays.txt -s ~/importation/robots/wp-pays.tsv
         *
     * @return unknown_type
     */
    private function lancerRobotWikipediaPays() {
                // Valeur spécifique de ce Robot
                $this->robot_nom = 'eFloreBotWp';
                $this->url_tpl = 'http://fr.wikipedia.org/wiki/%s';
                $this->regexp_ligne = '/<!-- bodytext -->(.*)<!-- \/bodytext -->/umsi';
                $this->regexps = array( 'Nom' => '<table class="infobox_v2" cellspacing="[^"]+" style="[^"]+">(?:\n|\r\n)<caption style="[^"]+"><b>(.*)<\/b>',
                                                                'Latitutde' => '<span class="geo-dec geo" title=".*"><span class="latitude">(.*)<\/span>',
                                                                'Longitude' => '<span class="geo-dec geo" title=".*">.*<\/span>, <span class="longitude">(.*)<\/span>',
                                                                'Superficie' => 'Superficie<\/a><\/b><\/td>(?:\n|\r\n)<td>((?:[0-9]|\s*|&#160;)+(?:,[0-9]+)?)(?:&#160;|\s*)km<sup>2<\/sup>',
                                                                'Population' => 'Population<\/a><\/b>(?:&#160;|\s)*<small>\([0-9]+\)<\/small><\/td>(?:\n|\r\n)<td>(.*)(?:&#160;|\s*)hab.',
                                                                'PopulationAnnee' => 'Population<\/a><\/b>(?:&#160;|\s)*<small>\(([0-9]+)\)',
                                                                'Capitale' => 'Capitale<\/a><\/b><\/td>(?:\n|\r\n)<td>(?:<a href="\/wiki\/[^"]+" title="[^"]+">|)(.+)<',
                                                                'PageWikipedia' => '(?:Ce document provient|Récupérée) de « <a href="http:\/\/fr.wikipedia.org\/wiki\/(.*)">'
                                                                );
        // Préparation des noms des pages
            foreach ($this->pages as $id => $nom) {
                $this->pages[$id] = str_replace(' ', '_', $nom);
            }
                $this->analyserUrls();
    }

        /**
     * Robot analysant une page de wikipedia à la recherche de plusieurs données par page.
     * Exemples d'utilisation :
         * /opt/lampp/bin/php script.php robot -p wp_commune -a wp -pgf ~/importation/robots/eFloreBotWp_INSEE_C.txt -s ~/importation/robots/wp_communes.tsv
         * /opt/lampp/bin/php script.php robot -p wp_commune -a wp -pg Montpellier -s ~/importation/robots/wp_communes.tsv
         * /opt/lampp/bin/php script.php robot -p wp_commune -a wp -pg Montpellier
         *
     * @return unknown_type
     */
    private function lancerRobotWikipediaListeCommunes() {
                // Valeur spécifique de ce Robot
                $this->robot_nom = 'eFloreBotWpListe';
                $this->url_tpl = 'http://fr.wikipedia.org/wiki/%s';
                $this->regexp_ligne = '/<tr>(.*)?<\/tr>/Uumsi';
                $this->mode = 'MULTI';
                $this->regexps = array( 'PageWikipedia' => '^<td align="left"><a href="\/wiki\/([^"]+)"',
                                                                'CodeInsee' => '^<td>.+<\/td>(?:\n|\r\n)<td>(\d+)<\/td>',
                                                                'CodePostal' => '^(?:<td>.+<\/td>(?:\n|\r\n)){2}<td>(\d+)<\/td>',
                                                                'Superficie' => '^(?:<td>.+<\/td>(?:\n|\r\n)){4}<td><span.+span>((?:[0-9]| |&#160;)+(?:,[0-9]+)?)<\/td>',
                                                                'Population' => '^(?:<td>.+<\/td>(?:\n|\r\n)){5}<td><span.+span>((?:[0-9]| |&#160;)+)<\/td>'
                                                                );
        // Préparation des noms des pages
            foreach ($this->pages as $id => $nom) {
                $this->pages[$id] = str_replace(' ', '_', $nom);
            }
                $this->analyserUrls();
    }

    /**
     * Robot analysant les pages du site de l'IPNI.
     * Exemples d'utilisation :
         * /opt/lampp/bin/php script.php robot -p ipni_auteur -a wp -pgf ~/importation/robots/eFloreBotIpni_auteur.txt -s ~/importation/robots/ipni_auteurs.tsv
         * /opt/lampp/bin/php script.php robot -p ipni_auteur -a wp -pg Z -s ~/importation/robots/ipni_auteurs.tsv
         * /opt/lampp/bin/php script.php robot -p ipni_auteur -a wp -pg Z
         *
     * @return unknown_type
     */
        private function lancerRobotIpni() {
        // Valeur spécifique de ce Robot
                $this->robot_nom = 'eFloreBotIpni';
                $this->url_tpl = 'http://ipni.org/ipni/advAuthorSearch.do?output_format=delimited&find_surname=%s*';
                $this->regexp_ligne = '/^(.*)$/umi';
                $this->regexps = array( 'Id' => '^([^%]+)%' ,
                                                                'Version' => '^(?:[^%]*%){1,}([^%]*)%' ,
                                                                'DefaultAuthorName' => '^(?:[^%]*%){2,}([^%]*)%' ,
                                                                'DefaultAuthorForename' => '^(?:[^%]*%){3,}([^%]*)%' ,
                                                                'DefaultAuthorSurname' => '^(?:[^%]*%){4,}([^%]*)%' ,
                                                                'StandardForm' => '^(?:[^%]*%){5,}([^%]*)%' ,
                                                                'NameNotes' => '^(?:[^%]*%){6,}([^%]*)%' ,
                                                                'NameSource' => '^(?:[^%]*%){7,}([^%]*)%' ,
                                                                'Dates' => '^(?:[^%]*%){8,}([^%]*)%' ,
                                                                'DateTypeCode' => '^(?:[^%]*%){9,}([^%]*)%' ,
                                                                'DateTypeString' => '^(?:[^%]*%){10,}([^%]*)%' ,
                                                                'AlternativeAbbreviations' => '^(?:[^%]*%){11,}([^%]*)%' ,
                                                                'AlternativeNames' => '^(?:[^%]*%){12,}([^%]*)%' ,
                                                                'TaxonGroups' => '^(?:[^%]*%){13,}([^%]*)%' ,
                                                                'ExampleOfNamePublished' => '^(?:[^%]*%){14,}([^%]*)$' );
                $this->analyserUrls();
    }

        /**
     * Robot analysant les pages du site de Cassini.
     * Exemples d'utilisation :
         * /opt/lampp/bin/php script.php robot -p cassini -a cassini -pgf ~/importation/robots/eFloreBotCassini.txt -s ~/importation/robots/cassini.tsv
         * /opt/lampp/bin/php script.php robot -p cassini -a cassini -pg 1 -s ~/importation/robots/cassini.tsv
         * /opt/lampp/bin/php script.php robot -p cassini -a cassini -pg 1
         *
     * @return unknown_type
     */
        private function lancerRobotCassini() {
        // Valeur spécifique de ce Robot
                $this->robot_nom = 'eFloreBotCassini';
                $this->url_tpl = 'http://cassini.ehess.fr/cassini/fr/html/fiche.php?select_resultat=%s';
                $this->encodage = 'ISO-8859-1';
                $this->regexp_ligne = '/\s*var\s+chaine1\s+\t=\s+"(.*?)";/umsi';
                $this->regexps = array( 'NomCommune' => '^([^\\\\]+)\\\\n' ,
                                                                'Superficie' => '\\\\nsuperficie;([^\\\\]+)\\\\n',
                                                                'AltitudeMin' => '\\\\naltitude;([^;]+);',
                                                                'AltitudeMax' => '\\\\naltitude;[^;]+;([^\\\\]+)\\\\n',
                                                                'LambertIIEtenduX' => '\\\\ncoordonnées;Lambert II étendu\\\\n;x;([^\\\\]+)\\\\n',
                                                                'LambertIIEtenduY' => '\\\\ncoordonnées;Lambert II étendu\\\\n;x;[^\\\\]+\\\\n;y;([^\\\\]+)\\\\n',
                                                                'Latitude' => '\\\\n;Latitude;(.+)?\\\\ncode',
                                                                'Longitude' => '\\\\n;Longitude;(.+?)\\\\n;Latitude',
                                                                'CodeInsee' => '\\\\ncode insee;([^\\\\]+)\\\\nstatut',
                                                                'Statut' => '\\\\nstatut\(s\);([^\\\\]+)\\\\n\\\\n');
                $this->analyserUrls();
    }

        /**
     * Robot analysant les pages du site de Cassini.
     * Exemples d'utilisation :
         * /opt/lampp/bin/php script.php robot -p cassini -a cassini -pgf ~/importation/robots/eFloreBotCassini.txt -s ~/importation/robots/cassini.tsv
         * /opt/lampp/bin/php script.php robot -p cassini -a cassini -pg 1 -s ~/importation/robots/cassini.tsv
         * /opt/lampp/bin/php script.php robot -p cassini -a cassini -pg 1
         *
     * @return unknown_type
     */
        private function lancerRobotUtmConverter() {
        // Valeur spécifique de ce Robot
                $this->robot_nom = 'eFloreBotUtmConverter';
                $this->url_tpl = 'http://www.rcn.montana.edu/resources/tools/coordinates.aspx?nav=11&c=DD&md=83&mdt=NAD83/WGS84&lat=%s&lath=N&lon=%s&lonh=E';
                $this->encodage = 'ISO-8859-1';
                $this->regexp_ligne = '/Universal Transverse Mercator \(UTM\):<\/td>(.*?)<\/table><\/td>/umsi';
                $this->regexps = array( 'UTM_Zone' => 'Zone: ([0-9]+)<' ,
                                                                'UTM_Est_x' => 'Easting: ([0-9]+)<',
                                                                'UTM_Nord_y' => 'Northing: ([0-9]+)<');
                $this->analyserUrls();
    }

    private function analyserUrls() {
        // Lancement de l'analyse
                $heure_debut = date('c',time());

                echo "Analyse de l'URL # : ";
                $pagesNum = 0;// Pages
                $lignesNum = 0;// Lignes
                $sortie = '';
                foreach ($this->pages as $paramsPage) {
                        $xhtml_page = $this->getHtml($this->url_tpl, $paramsPage);
                        $xhtml_lignes = $this->extraireChaine($this->regexp_ligne, $xhtml_page);

                        // Pour chaque chaine début/fin trouvées au sein de la page, nous recherchons les regexps des données.
                        if (count($xhtml_lignes) > 1 && $this->mode != 'MULTI') {
                                $this->traiterAttention("Plusieurs lignes correspondent à votre expression régulière de limitation du contenu :\n %s", array($this->regexp_ligne));
                        } else if ($xhtml_lignes) {
                                print_r($xhtml_lignes );
                                foreach ($xhtml_lignes as $xhtml) {
                                        $champsNum = 1;// Champs
                                        $ligne = '';
                                        foreach ($this->regexps as $chp => $regexp) {
                                                // Si nous traitons la première ligne nous ajoutons les noms des champs en début de fichier de sortie
                                                if ($lignesNum == 0) {
                                                        $sortie .= $chp.(($champsNum == count($this->regexps)) ? "\n" : "\t");
                                                        $champsNum++;
                                                }
                                                // Ajout de la valeur trouvée grâce à l'expression régulière à la ligne de sortie
                                                if (preg_match('/'.$regexp.'/Umsi', $xhtml, $match)) {
                                                        $ligne .= $this->nettoyer($match[1])."\t";
                                                } else {
                                                        $ligne .= "\t";
                                                }
                                        }
                                        $lignesNum++;
                                        $ligne = trim($ligne);
                                        $sortie .= $ligne."\n";

                                        // Affichage en console...
                                        if (empty($this->sortie)) {
                                                echo "\t".$ligne."\n";
                                        }
                                }

                        } else {
                                $this->traiterAttention("Impossible de trouver les chaines début et fin dans la page «%s» avec la regexp :\n%s", array($this->getNomPage($paramsPage), $this->regexp_ligne));
                        }
                        // Affichage en console...
                        echo str_repeat(chr(8), ( strlen( $pagesNum ) + 1 ))."\t".$pagesNum++;
                }
                echo "\n";
                $heure_fin = date('c',time());

                // Ajout de métadonnées
                $metadonnees = "Début d'importation : $heure_debut\nFin d'importation : $heure_fin\n";
                $metadonnees .= "Source des données : ".$this->url_tpl."\n";
                $sortie = $metadonnees.$sortie;

                // Écriture du fichier de sortie ou retour dans la console
                if (!empty($this->sortie)) {
                        file_put_contents($this->sortie, $sortie);
                }
    }

    private function nettoyer($txt) {
        $txt = trim($txt);
        $txt = preg_replace('/(?:&nbsp;|&#160;)/', ' ', $txt);
        return $txt;
    }

    private function getHtml($url_tpl, $paramsPage) {
                // Lancement en ligne de commande pour tester :
                //curl -v --url "http://fr.wikipedia.org/wiki/Montpellier" --config /home/jpm/web/eflore_bp/consultation/scripts/modules/robot/configuration/cookieconf.txt
                if ($this->cache && file_exists($fichier_cache = $this->cache.$this->getNomPage($paramsPage))) {
                        $html = file_get_contents($fichier_cache);
                } else {
                $url = vsprintf($url_tpl, $paramsPage);
                $this->traiterAttention(" Url : ".$url);
                // Initialisation CURL
                $curl = curl_init();
                    curl_setopt($curl, CURLOPT_COOKIEFILE, $this->cookie);
                    curl_setopt($curl, CURLOPT_URL, $url);
                    curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
                    curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Linux x86_64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1');

                    $html = curl_exec($curl);

                    // Mise en cache
                    if ($this->cache) {
                        file_put_contents($fichier_cache, $html);
                    }
                }

                // Nettoyage des entités html
                $html = $this->encoderUtf8($html, 'HTML-ENTITIES');
                // Nettoyage de l'encodage des urls
                $html = urldecode($html);

                // Convertion en UTF-8 si nécessaire
                if ($this->encodage) {
                        $html = $this->encoderUtf8($html, $this->encodage);
                }

            return $html;
        }

        /**
         * Méthode récupérant seulement une partie du texte passé en paramétre.
         *
         * @param $debut chaine de caractère indiquant le début du texte à prendre en compte.
         * @param $fin chaine de caractère indiquant la fin du texte à prendre en compte.
         * @param $txt le texte duquel extraire une partie bornée par $debut et $fin.
         * @return le texte extrait.
         */
        private function extraireChaine($regexp, $txt) {
            if (preg_match_all($regexp, $txt, $match)) {
                return $match[1];
            } else {
                return false;
            }
        }
        /**
         * Charge les lignes d'un fichier dans un tableau et le retourne.
         * Supprime les caractères blancs et les nouvelles lignes.
         *
         * @param $fichier
         * @return unknown_type
         */
        private function convertirFichierEnTableau($fichier) {
                $tableau = array();
                $handle = fopen($fichier,'r');
                if ($handle) {
                        while ($ligne = fgets($handle)) {
                                $tableau[] = explode("\t", trim($ligne));
                        }
                        fclose($handle);
                }
                return $tableau;
        }

        private function getNomPage($paramsPage) {
                return str_replace(' ', '_', implode('_', $paramsPage)).'.html';
        }

}
?>