Laisser un commentaire

Créer un crawler web en PHP

Sur le web, les robots, spiders ou crawlers, selon les termes, sont très employés. Google, bien sur, en utilise pour explorer les sites à référencer, mais leur utilisation ne s’arrête pas là. Facebook aussi a recours à eux lorsque vous postez un lien et qu’une image et la description s’affichent sur le mur, on pourrait penser également aux portails d’informations (ou les applis comme Flipboard) qui vont chercher les contenus sur divers sites, mais encore Pinterest qui est très à la mode… La liste est longue. Apprenons à programmer le notre pour nos propres besoins !

Notre objectif

Puisqu’il faut bien prendre un exemple, le robot que nous allons construire vise à crawler un site pour en extraire les adresses email. Cependant, une fois que vous aurez compris comment il marche, vous pourrez facilement le modifier pour l’adapter à d’autres besoins.

Prérequis

Nous allons avoir besoin de php en cli et de curl pour php. S’ils ne sont pas déjà présent sur votre machine, il va falloir les installer :

#Pour linux
#php en cli
sudo apt-get install php5-cli

#curl pour php
sudo apt-get install php5-curl

Sur mac, vous avez déjà tout il me semble, et pour windows… pour windows, euh allez voir sur google. Ou installez linux dans une machine virtuelle !

Au niveau de vos connaissances, si vous voulez comprendre ce qu’on fait, il faudra connaitre l’html (c’est évident) et le php, au moins un minimum.

Aperçu du fonctionnement et des étapes

Avant de se jeter dans le code, voyons quelles sont les étapes que notre bot va accomplir pour nous générer ce petit fichier d’emails.

fonctions du crawler php

Il est pas beau mon schéma ? Si on détaille étape par étape, voilà ce qu’on obtient :

  1. connection au site à crawler (en général la homepage)
  2. récupération du contenu de la page en cours
  3. Analyse du contenu récupéré :
    • extraction des emails pour le fichier email
    • extraction des liens internes pour le fichier liens (si on veut pouvoir visiter d’autres pages du site)
  4. récupération du lien de la prochaine page à explorer et refaire une boucle

À partir de ces étapes, voilà ce que nous allons faire.

Connection au site et récupération du contenu de la page

On commence par le commencement. C’est là que curl entre en jeux. Il faut qu’on se connecte au site, et qu’on récupère le contenu de la page. On va le mettre dans un fichier pour pouvoir l’analyser dans un second temps. On va créer une fonction crawl pour y placer certaines étapes et pouvoir la rappeler pour chaque nouvelle url.

Analyse et extraction du contenu

On va ensuite récupérer le contenu crawlé dans une variable et extraire les emails grâce à une regex et la fonction preg_match_all. Elle nous renvoit tout ce qu’elle trouve dans un array, on fait donc une boucle pour inscrire les mails ainsi récupérés dans un fichier. On procède ensuite de la même manière pour les liens.

Pour les liens, on vérifie d’abord si le fichier existe afin de savoir si on vérifie les doublons. Sur un site web, avec le footer et le menu, on va retrouver les mêmes liens sur un certain nombre de pages. Si on ne vérifiait pas les doublons on visiterait plusieurs fois les mêmes pages, ce qui est vraiment désastreux en terme de performances. On aurait pu faire cette vérification pour les emails, mais comme ça ne joue pas sur les performances, on peut traiter ça à posteriori.

Rappel de la fonction avec une nouvelle url

Notre fonction crawl est maintenant terminée. On va créer une boucle qui parcourt le fichier contenant les liens. Elle rappelle la fonction pour chaque lien, et elle s’arrête lorsque tout le fichier à été parcouru.

On a maintenant une bonne idée des différentes étapes à accomplir. C’est donc l’heure de sortir votre éditeur de code.

3, 2, 1, à vos claviers, programmez !

J’ai commenté presque chacune des lignes, avec les explications fournies ci-dessus, vous devriez pouvoir tout comprendre sans aucun soucis.

# Buzut – https://buzut.fr – 04/2012
# GPL – http://www.gnu.org/licenses/gpl.html

#!/usr/bin/php

<?php

// site web à crawler
$url = 'http://www.site.com';

// déclaration de la fonction de crawl
function crawl($url) {

  // initialisation de curl
  $ch = curl_init($url);

  // création d'un fichier texte pour stocker le contenu crawlé
  // effacement du fichier précédent si existe
  if(file_exists('fichier_html_brut')) {
    unlink('fichier_html_brut');
  }

  $fp_fichier_html_brut = fopen('fichier_html_brut', 'a');

  // définition des paramètres curl
  // 1 redirection de l'output dans le fichier txt
  curl_setopt($ch, CURLOPT_FILE, $fp_fichier_html_brut);

  // 2 on spécifie d'ignorer les headers HTTP
  curl_setopt($ch, CURLOPT_HEADER, 0);

  // exécution de curl
  curl_exec($ch);

  // fermeture de la session curl
  curl_close($ch);

  // fermeture du fichier texte
  fclose($fp_fichier_html_brut);

  // passage du contenu du fichier à une variable pour analyse
  $html_brut = file_get_contents('fichier_html_brut');

  // extraction des emails
  preg_match_all('#"mailto:.+"#U', $html_brut, $adresses_mail);

  // creation d'un fichier pour recevoir les mails
  $fp_fichier_emails = fopen('fichier_mails', 'a');

  // on créé une boucle pour placer tous les mails de la page dans le fichier
  foreach ($adresses_mail[0] as $element) {
    // on "nettoie" les mails en enlevant les guillemets et le "mailto:"
    // on passe donc de "mailto:addr@gmail.com" à addr@gmail.com
    $element = preg_replace('#"#', '', $element);
    $element = preg_replace('#mailto:#', '', $element);

    // on ajoute un retour chariot en fin de ligne pour avoir 1 mail/ligne
    $element .= "n";
    fputs($fp_fichier_emails, $element);
  }

  fclose($fp_fichier_emails);

  // extraction des liens
  preg_match_all('#"/?[a-zA-Z0-9_./-]+.(php|html|htm)"#', $html_brut, $liens_extraits);

  // si le fichier contenant les liens existe déjà
  if (file_exists('liens_a_suivre')) {        
    // on l'ouvre
    $fp_fichier_liens = fopen('liens_a_suivre', 'a');

    // on créé une boucle pour enregistrer tous les liens ds le fichier
    foreach ($liens_extraits[0] as $element) {        
      // on recharge le contenu dans la variable à chaque tour de boucle
      // pour être à jour si le lien est present +sieurs x sur la même page
      $gestion_doublons = file_get_contents('liens_a_suivre');

      // on enlève les "" qui entourent les liens
      $element = preg_replace('#"#', '', $element);
      $follow_url = $element;
      $follow_url .= "n";

      // creation d'un pattern pour la verification ds doublons
      $pattern = '#'.$follow_url.'#';

      // on verifie grace au pattern précédemment créé
      // que le lien qu'on vient de capturer n'est pas déjà ds le fichier
      if (!preg_match($pattern, $gestion_doublons)) {
          fputs($fp_fichier_liens, $follow_url);
      }
    }
  }

  // si le fichier contenant les liens n'existe pas
  else {
    // on le créé
    $fp_fichier_liens = fopen('liens_a_suivre', 'a');

    // puis on fait une boucle pour enregistrer tous les liens ds 1 fichier
    foreach ($liens_extraits[0] as $element) {
        $element = preg_replace('#"#', '', $element);
        $follow_url = $element;
        $follow_url .= "n";
        fputs($fp_fichier_liens, $follow_url);
    }
  }

  // fermeture du fichier contenant les liens
  fclose($fp_fichier_liens);
}

// on appelle une première fois la fonction avec l'url racine
crawl($url);


// ensuite on ouvre le fichier de liens pour visiter les autres pages du site
$lire_autres_pages = fopen('liens_a_suivre', 'r');

// on créé une boucle pour visiter chacun des liens
// on stop cette boucle quand le curseur arrive à la fin du fichier
$numero_de_ligne = 1;

while(!feof($lire_autres_pages)) {
  // curl ne comprend que les liens absolus
  // on formate donc nos liens relatifs en liens absolus
  $page_suivante = $url;
  $page_suivante .= fgets($lire_autres_pages);
  echo $numero_de_ligne . ' Analyse en cours, page : ' .  $page_suivante;
  $numero_de_ligne++;

  //on se contente de rappeler la fonction crawl avec nos nouveaux liens
  crawl($page_suivante);
}

fclose ($lire_autres_pages);
?>

Ce crawler est fonctionnel mais il pourrait grandement être amélioré. Libre à vous de l’utiliser comme bon vous semble. Il est sous licence GNU-GPL donc ne vous privez pas de l’améliorer et d’en faire profiter les autres !

Attention à l’url que vous fournissez. Si les liens relatifs du site sont du type nouvelle-page.php, mettez un / à la fin de l’adresse racine : “http://site-a-crawler.com/". Si les adresses ont déjà un slash au début, n’en rajoutez pas à la fin de votre adresse racine.

Un monde de possibilités s’ouvre maintenant à vous ! Qu’allez-vous faire avec ce robot ?

PS, selon vos besoins, vous pouvez peut-être utiliser Photon, un crawler python bien plus abouti que celui que nous avons développé ici.

Commentaires

Rejoignez la discussion !

Vous pouvez utiliser Markdown pour les liens [ancre de lien](url), la mise en *italique* et en **gras**. Enfin pour le code, vous pouvez utiliser la syntaxe `inline` et la syntaxe bloc

```
ceci est un bloc
de code
```