Guide

Come Realizzare Crawler da PHP-CLI

Oggi vi voglio parlare di Crawler scritti in PHP, anche se in un articolo precedente ho già affrontato questo argomento.
Partiamo dall’esigenza che mi ha spinto a costruire questo piccolo spider in php: devo popolare nella cache le pagine del mio sito…
Soluzione intrapresa: leggo tutte le pagine (o meglio tutti gli url all’interno del mio dominio) in un momento di basso traffico (la notte).

Il primo problema da affrontare è stato l’esecuzione di script php da riga di comando, come fare?.

In Php è possibile eseguire tutti gli script normalmente eseguiti da apache e php anche a riga di comando tramite PHP CLI.

L’acronimo CLI è l’abbreviazione di Command Line Interface (‘Interfaccia da Linea di Comando’), una modalità di accesso al sistema attraverso un interprete per comandi testuali; questi comandi sono espressi essenzialmente attraverso delle stringhe alfanumeriche che possono essere eseguite da altre applicazioni.

Nel caso di PHP, per esempio, sarà possibile utilizzare le CLI per integrare determinati programmi con funzionalità messe a disposizione da questo linguaggio.

Le differenze tra il tradizionale sviluppo di pagine Web e l’utilizzo di PHP tramite interfaccia testuale non sono poche, è possibile però elencarne le principali:

l’output generato tramite CLI è “pulito” (di default non vengono introdotti degli headers), l’utilizzatore lo vede esattamente così com’è cioè nel modo in cui viene prodotto;
l’output presenta una formattazione minima se non assente, il formato in “semplice testo” permette una più semplice elaborazione da parte di programmi di terze parti;
il tempo di esecuzione dei comandi è solitamente molto più veloce di quello richiesto nel caso in cui debba essere caricata una pagina Web;
l’utilizzatore può agire dall’interno dello spazio fisico dedicato a PHP nel file system;
gli input vengono gestiti a livello di runtime e cioè nel momento in cui un programma viene eseguito, cosa non possibile per le pagine Web dove la definizione degli input precede l’esecuzione dello script.
In molti potrebbero chiedersi come mai si è sentita l’esigenza di integrare in PHP la possibilità di inviare istruzioni tramite linea di comando, esistono infatti linguaggi molto più collaudati come per esempio Perl grazie ai quali interagire con il sistema. L’obiezione è sensata, ma nello stesso tempo è necessario elencare i vantaggi derivanti dall’introduzione della CLI in questo linguaggio:

non è necessario imparare altri linguaggi come appunto Perl, Bash o Awk;
consente di riutilizzare componenti e codici preesistenti;
mette a disposizione uno strumento per sfruttare al meglio il multithreading in PHP5;
permette di integrare nuove funzionalità in applicazioni PHP preesistenti;
introduce la possibilità di automatizzare e programmare (CRON) i processi, cosa impossibile tramite il tradizionale sviluppo limitato alle pagine Web;
consente di creare interfacce grafiche (GUI) utilizzando PHP e GTK.
Se volete maggiori dettagli su PHP Cli potete vedere questo tutorial in inglese.

Data questa piccola introduzione a PHP CLI vediamo in dettaglio come costruire l’algoritmo di ricerca delle pagine del Crawler. L’idea è quella di usare una struttura dati ti tipo Coda FIFO (FIFO Queue), dove memorizzare tutti gli url del sito mano a mano che vengono reperiti.

Recupero gli url dalla home page del sito e li salvo nella coda (urls).

Setto il contatore di avanzamento a zero (index=0).

Prendo l’url i-esimo nella coda (urls[index]) dalla coda e lo leggo la pagina ricavando nuovi url.

Salvo gli url nella coda se appartengono al dominio del sito e se non sono già presenti nella coda.

Incremento il contatore di avanzamento (index++).

Riprendi l’url i-esimo nella coda (urls[index]) dalla coda e leggo la pagina ricavando nuovi url.

Proseguo finchè il contatore è minore della dimensione della coda (index < count(urls) -1).
Cosi facendo sono sicuro di processare tutti gli url presenti nel sito.
Vediamo l’implementazione del crawler in PHP:

/*
Script Name: Crawler.php
Description: Un semplice crawler in php per la lettura di tutte le pagine di un sito da lanciare da PHP CLI
Autore: Gianluca Moroni http://www.gianjey.it
Autore URI: http://www.programmandofacile.it
Version: 1.0
*/

function LeggiUrls($urlPagina,$urlDominio,&$queue) {
$result = file_get_contents($urlPagina);
// Se ho scaricato qualcosa
if( $result )
{
// Cerco gli href sulla pagina
preg_match_all( ‘/<a.+?href=”(http:\/\/www.[^0-9].+?)”/’, $result, $output, PREG_SET_ORDER );

// Ciclo tutti i risultati e verifico se gli url sono nel dominio del sito
// e li aggiungo alla coda se non erano già presenti
foreach( $output as $item )
{
//print_r($item[1]);
$host = parse_url($item[1], PHP_URL_HOST);
if($host == $urlDominio) {
if (!in_array($item[1], $queue)) {
array_push($queue,$item[1]);
}
}
}
}
}

// La coda che contiene gli url
$queue = array();

// Leggo il sito dallo riga di comando, deve avere http:// davanti al dominio
$urlSito = $_SERVER[“argv”][1];
$host = parse_url($urlSito, PHP_URL_HOST);

// Variabile per ritardare le chiamate
// se non passata da riga di comando viene settata ad un secondo
$DELAY = 1;

if(isset($_SERVER[“argv”][2])) {
$DELAY = $_SERVER[“argv”][2];
}

// Leggo gli url della prima pagina e li aggiungo alla coda
LeggiUrls($urlSito,$host,$queue);

$index=0;

// Ciclo e recupero tutte le pagine nella coda lanciando
// la funzione che mi cerca gli url e li aggiunge alla coda
// se nel dominio e se non presenti nella coda
while($index < count($queue)-1) { LeggiUrls($queue[$index],$host,$queue); $index++; sleep($DELAY); echo(“Pagina:” . $queue[$index] . “\n”); echo(“Index:” . $index . “\n”); echo(“Count:” .count($queue) . “\n”); } print_r(‘Finito’); ?>

Per lanciare lo script salvate la pagina in un file chiamato ad esempio “Crawler.php”, date i permessi di esecuzione se siete sotto Linux, e scrivete a riga di comando:

php Crawler.php http://www.sito.it 5
Cosi facendo lanciate lo spider verso http://www.sito.it con un intervallo di 5 secondi tra una chiamata e l’altra, ed ecco che il gioco e fatto, tutte le pagine del sito verranno lette e la cache verrà popolata.

Partendo da questo semplice esempio, e giocando un po con le regular expression potrete costruire crawler che recuperano tutto quello che vi serve.
Spero di essere stato chiaro nell’esposizione, se avete dubbi non esitate a contattarmi.