53 commentaires

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

qwerty dit –

Sympa. Après, il faut stocker les données dans une BDD pour faire un moteur de recherche et tout le tralala...

Elmoutaouakkil dit –

Salut, SVP comment je peux relier les données extraites à ma base de données ?

Buzut dit –

oui oui tout a fait ! Cependant le but ici n'était pas de faire un moteur de recherche mais juste de récolter des emails, qu'on stocke dans un fichier (on pourrait aussi bien les mettre en BDD, mais ça n'est pas ici l'objet).

Quoi qu'il en soi, pour faire un "vrai" moteur de recherche, en plus d'un code un peu plus poilu que celui-ci, il faudrait aussi une infrastructure TRES TRES solide derrière si on compte se lancer dans l'exploration du web !!

Ely dit –

Salut buzut Super intéressant mais ma problématique est la suivante Site marchant de 3mois existence je fait du Google adword Je me positionne en 2 places lors que j'ai des visites sans achat Est il possible de récupérer l adresse mail du visiteur pour le relancer avec une promo Tu peux faire un programme dans l idée je suis très intéresser contact par mail et +++++ Merci de ton retour

Buzut dit –

Bonjour,

Ce que tu veux faire ne peut se faire avec Google Adwords dans la mesure où on n'a aucun moyen d'identifier les internautes qui viennent sur un site par le biais de Google… On peut identifier un visiteur qui est déjà passé sur un site, mais ça s'arrête là, et si on n'a aucune coordonnées relative à cet internaute, il est impossible de les créer ex-nihilo.

En revanche, il est tout a fait possible d'imaginer un formulaire pour prendre les coordonnées de notre visiteur, même s'il ne produit aucun achat.

Qui plus est, une pratique qui peut être intéressante, qui doit être adossé à un emailing, est de faire du tracking sur ses visiteurs. Grace à l'emailing personnalisé que tu envoyes, tu peux savoir quel visiteur clique sur ton mail et se rend sur ton site, quelles sont les pages qu'il visite et combien de temps il y passe. De cette manière, s'il n'a produit aucun achat, tu es en mesure de faire un suivi commercial personnalisé (relance email, appel téléphonique si tu as son numéro etc).

Patrick dit –

Hello Buzut super interessant ton crawler, je me demande s'il est possible de récupérer de l'info boursière/forex pour en faire une moyenne... je m'explique: je crawl un site pour extraire l'info des traders (qui est publique) a savoir leur position ouverte et le style ( achat ou vente) par la suite tu fais une moyenne ce pourrais-être très utile dans le monde financier.

Buzut dit –

Salut Patrick !

Oui il est tout a fait possible de faire ca ! Ca demenderais d'apporter quelques changements au robot mais cest tout a fait faisable.

Une fois sur la page sur laquelle tu as les infos qui t'interessent, il ne reste plus qu'a ecrire la bonne REGEX pour extraire ces infos et le plus dur est fait :)

Patrick dit –

et que serait la bonne regex... je suis quand même assez nul en php, je me debrouille un peu en html sans plus...

Merci

Buzut dit –

si tu maitrises seulement le html tu risques de rencontrer quelques problèmes car il faut quand meme apporter certaines modifications au robot. quant a la REGEX, il faudrait voir le site en question pour voir la forme des infos a capturer et comment est le code html autour.

Concernant les mails c'est relativement simple puisque la balise html de mail est toujours de la forme ...

James dit –

Salut Buzut. J'ai fais un simple copié-collé pour tester, avec comme url de référence un site bien fourni en contenu, et le fichier "liens_a_suivre" ne se remplit pas. Il y a une ligne en particulier où l'on doit faire une modif ? En tout cas merci pour l'article, j'aime ce Web qui partage ! ^^

Buzut dit –

Bizarre bizarre !!

Faudrait peut-être jeter un œil au format des liens dans le html du site. Comme je le dis en bas, mon crawler est très loin d'être parfait et il ne prend en charge beaucoup de formats…

Le pattern est le suivant '#"/?[a-zA-Z0-9_./-]+\.(php|html|htm)"#', donc si tes liens sont en absolu (type http://www.monsite.com/une_autre_page.php), ils ne seront pas pris en compte. De même, les cas où il y a de la réécriture d'url n'est pas pris en compte, une url du type http://www.monsite.com/mon_contenu/ sans extension html, htm ou php ne sera pas non plus prise en compte !

De même si l'extenion est .asp par exemple, ça ne matchera pas…

Enfin tout ça peut s'adapter, ce n'est pas grand chose, mais c'est à savoir ! Le fichier "fichier_html_brut" se remplit de contenu au moins (sinon le pb est à un autre niveau) ?

A bientôt ;)

James dit –

Oui "fichier_html_brut" se remplit de contenu, ce qui permet de "voir" comment les robots voient le site, cela m'a donné quelques informations intéressantes. Je vais faire d'autres tests avec des liens en relatifs histoire de lever le doute.

Merci !

Emmanuel Loisance dit –

Merci pour ce script, je vais tenter de le modifier pour en faire un script de sitemap automatique. J'ai déjà un très bon début grâce à toi !

thomas dit –

Bonjour,

je suis en train de créer un crawler similaire au tient tout en PHP sans utiliser curl. (dont je ne vois pas l'intérêt ici)

Si cela t'interesse, ce serait cool d'apporter tes suggestions sur mon projet : https://github.com/thomasmds/PHPCrawler/

Buzut dit –

Salut, Merci de ton commentaire, j'irai jeter un œil plus approfondi à ton projet qui qui m'a l'air intéressant. Quant à CURL, c'est vrai qu'il ne présente aucun intérêt ici, en revanche, il peut être utile pour crawler certains sites qui nécessitent un user-agent, le remplissage d'un formulaire, la présence d'un cookie etc… contrairement à file_get_contents qui ne présente pas d'options.

pat dit –

Bonjour,

Oui c'est intéressant. En plus ce que je voudrais c'est que le robot clique sur un bouton dans la page. Comment faire ?

Bien cdlt

Buzut dit –

Bonjour,

tout dépend du bouton ! Derrière le bouton, il peut s'agir d'un simple lien, ou d'un formulaire, dans le premier cas, le robot suivra naturellement le lien, dans le second, il faudra configurer spécifiquement CURL pour valider le formulaire.

ben dit –

Bonjour,

Juste une question bête : comment lancé le crawl ?

Merci

ben dit –

Ok par contre le fichier générés par ce robot sont vies ?? :(

J'ai juste copier votre code et changé l'url du site a crawlé pour tester ?

Buzut dit –

Normalement non. L'un doit contenir le code html de la page en cours d'analyse, l'autre les liens à analyser et le troisième les adresses mails. Mais le robot n'est pas très perfectionné et tout dépend donc du format des urls sur le site (relatives, absolues, avec slash au début ou pas…)

MagiCrazy dit –

Hey,

j'vais mettre mon grain de sel pour une fois dans du code ! ;) Il y a plusieurs façons d'extraire des informations dans une page web, les expressions rationnelles, comme tu en as parlé, mais une autre méthode peut s'avérer extrêmement pratique (et bien plus rapide) si on sait exactement ce qu'on cherche, et surtout on le cherche, j'ai nommmé, le XPath ! En résumé, cela permet de donner un chemin d'accès, absolu ou relatif à évaluer, et le moteur XPath va trouver les occurrences de ce qui vous intéresse, et vous retourner un set de nodes.

Je fais ça régulièrement en ruby avec nokogiri, mais il existe un support dans tous les langages. Pour PHP : http://php.net/manual/fr/class.domxpath.php

WENKZ dit –

Salut! je viens de créer un script curl qui me permet de recuperer le contenu de balise, mon script fonctionne mon seul soucis c'est que je ne sais pas comment sauvegarder ce que le script sort, je me suis servis de DOMdocument et de CURL pour extraire les données dont j'ai besoin, par la suite je voudrai automatisé tout cela comme un crawler... mais j'ai besoin d'aide ^^ ty de ta réponse !! ciao tous

Buzut dit –

Et bien pour sauvegarder ce que le script sort, tu rediriges le contenu dans une variable, et ensuite soit tu stockes ce contenu dans une base de données, soit dans un fichier texte (fopen, fputs et fclose). Enfin, pour automatiser ton script, tu peux l'installer sur un petit serveur linux et mettre un cron qui le lance tous les x instants ;)

WENKZ dit –

j'ai créer un crawler avec cURL et DOMdocument, je voudrai passer par une page formulaire dans la quelle il y aura un seul onglet a remplir pour y mettre l'url des site a crawler cette page je l'ai deja créé mais je ne sais pas comment récuperer ces url dans mon fichier script.php et generer un nouveau code avec l'url

WENKZ dit –

j'ai ce bout de code que l'on ma donné : [code] &lt;?PHP

// Recup url if (isset($url)) { $url=$_POST[&#039;url&#039;]; } else { $url = &#039;&#039;; }

// traitement url if ($url '') { // le traitement qui stocke l'url dans un fichier } else { echo 'url vide ou premier affichage'; }

// affichage de la page ?&gt;

&lt;input type=&quot;text&quot; name=&quot;url&quot; value=&quot;" /&gt;

[/code]

et mon crawler que j'ai créé

[code] //recuperation du contenu require_once 'simple_html_dom.php'; $html = new simple_html_dom(); $html-&gt;load_file('www.domain.fr'); foreach($html-&gt;find('.pright') as $post){ echo 'ARTICLE : '.''.$post-&gt;find('.panel_titre',0)-&gt;plaintext.''.''; echo 'STOCK : '.''.$post-&gt;find('.stock_in',0)-&gt;plaintext.''.''; echo 'PRIX : '.''.$post-&gt;find('#price',0)-&gt;plaintext.''.''; } [/code]

Rakabulle dit –

Super ton crawler ! Merci :)

Alexandre dit –

Bonjour

Est si l'on ne connait pas le site web ou aller crawler? Je cherche à faire un moteur qui cherche des informations partout sur les sites français du web mais sans savoir lesquels a priori, car c'est pour connaitre le nombre de fois où une occurrence (keyword) est citée, et lorsque elle apparait dans une page on souhaite rapatrier le contenu de la page dans une base (pour ensuite faire du text mining) : est ce possible sans avoir à scrapper Google?

Merci

Buzut dit –

Si tu ne connais pas le site et que tu n'as pas une liste de sites préétablie, tu commences par explorer un site puis tu suis les liens sortant et tu navigues vers d'autres sites. C'est comme ça que procède google.

Ensuite si c'est le marché français qui t'intéresses, tu peux viser seulement les sites en .fr, où alors regarder à chaque fois si l'ip correspondant aux nom de domaine est une ip française.

C'est du boulot et derrière il faut aussi avoir des serveurs qui vont pouvoir faire le travail. Parce que si tu te lances dans le crawl du web français, y'a quand même quelques milliards de pages à explorer !

d3cima dit –

Un conseil pour ceux qui souhaitent faire ce Crawler PHP :

L'HTML, c'est tout simplement bêtement du XML, et comme le XML, on peut le transformer en arbre DOM et donc de ce fait, il suffit d'un peu de requête XPath et récupérer le contenu d'un site web devient simple :-)

Buzut dit –

Tu as raison, le XPAth peut être une très bonne solution. Cependant, comme le dit Magicrazy un peu plus haut, il faut savoir où se trouve l'information dans ton DOM, ce qui n'est pas toujours évident ;)

Yuitre dit –

Bonjour,

je n'arrive pas à faire fonctionner ce script ; les trois fichiers textes sont créés puis la page du navigateur se fige avec ce texte : 1 Analyse en cours, page : http://www.site.com J'ai essayé avec plusieurs url. Savez tu d'où pourrait provenir le souci ?

max dit –

S' il vous plais Comment je peux tester ce petit crawler

max dit –

S' il vous plais Comment je peux tester ce petit crawler ??

max dit –

Je veux créer un crawler qui fait la mise a jour de la base de données quotidiennement de mon futur site et je me pose la question ou va se trouver le script .est ce dans mon ordinateur ou va être éxecuter dans le serveur d hébergement??

Buzut dit –

Tu dois le faire tourner depuis ton serveur !

Seulement, si tu es sur un hébergement mutualisé, ce n'est pas certain que ça fonctionne puisque curl doit être activé.

max dit –

Merci beaucoup Buzut pour ta reponse

lud piv dit –

Bonjour, Y a til un moyen de creer un bot pour recuperer sur des infos de contact (adresse, tel ecc) d' un type d'etablissement en particulier? Combine de temps cela prendrait t il

lud piv dit –

je precise, que ne je possede aucune competence pour creer de robot.

Buzut dit –

Il est tout a fait possible de créer un robot pour récupérer des infos de contacts. Il faut voir quels sites proposent de récupérer ces infos et comment elles sont structurées. C'est en fonction de cela qu'on programmera la robot !

Wordpress, vaccin contre le piratage - Buzut | Axxcom | Scoop.it dit –

[&#8230;] Comment créer un crawler web en php - Buzut. Sur le web, les robots, spiders ou crawlers, selon les termes, sont très employés. [&#8230;]

armel dit –

votre code est super interessant mais comment rediriger les extractions dans des fichiers Excel???

Buzut dit –

Excel peut importer des fichiers CSV, il faut donc simplement séparer les emails par un ";"

dramane dit –

Bonjours intéressant ton article,mais j'aimerai savoir s'il était possible d'avoir ton robot en Python, car je fais du python et avoir une idée de crawler de ce genre m'aiderait beaucoup pour certains de mes projets. encore merci

Buzut dit –

Salut !

Le crawler se fait sans aucun problème dans la plupart des langages. La logique reste exactement la même, tu as juste besoin d'adapter le nom des fonctions utilisées pour qu'elles correspondent à celles de Python. Je ne peux pas t'aider pour ça, mais si tu fais déjà du Python, cherches dans la doc, tu trouveras sans mal…

dramane dit –

ok , merci quand même pour ta réponse , c'est déjà ça j'ai déjà commencé à m'y mettre sur la doc et le soucis maintenant concerne les expressions régulière pour je pensais pouvoir y échapper

Buzut dit –

Selon ce que tu veux crawler, ça risque d'être incontournable les REGEX. Si tu sais exactement où est ce que tu veux dans le code, il y a xpath (supporté par les libs xml) http://buzut.fr/2015/04/06/extraire-des-informations-du-dom-en-php/

Mais sinon, les REGEX sont toujours incontournable dans tous les langages et pour de nombreuses choses. Au cas où, un condensé de rappel http://buzut.fr/2012/05/21/la-puissance-des-regex/

dramane dit –

ah bon merci pour l'info sur xpah c'est des adresses mails que je veux crawler

Eyecom-dz dit –

super sympa ce script je test de suit

max6938 dit –

Salut, ce system est t'il legal ?

Buzut dit –

Hello, le système en lui-même est tout à fait légal. C'est l'usage que tu en feras qui peut être illégal.

Si tu récupères des mails de particuliers et que tu les spams sans leur consentement, c'est illégal (en France et dans la majorité des pays de l'UE au moins)

max6938 dit –

si tu recupere des donné juste pour des test (lien, photo, ext) est'ce toujours legal

Minkizz dit –

C'est bien, mais c'est très lent ! J'arrive à peine à crawler 1 page par minute

Buzut dit –

Étrange… Soit ta connexion est très lente, soit le site en question est lent ou il te bride volontairement.

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
```