it-swarm.dev

Progettare un web crawler

Mi sono imbattuto in una domanda dell'intervista "Se stavi progettando un web crawler, come eviterai di entrare in loop infiniti?" E sto cercando di rispondere.

Come tutto inizia dall'inizio ... Diciamo che Google ha iniziato con alcune pagine del mozzo che ne dicono centinaia (come sono state trovate queste pagine hub in primo luogo è una diversa sotto-domanda) ........ Come Google segue i collegamenti da una pagina e così via, continua a creare una tabella hash per assicurarsi che non segua le pagine visitate in precedenza.

Cosa succede se la stessa pagina ha 2 nomi (URL) dire in questi giorni in cui abbiamo URL accorciatori ecc.

Ho preso Google come esempio. Anche se Google non perde di vista il funzionamento degli algoritmi del crawler Web e del ranking delle pagine, ma qualche ipotesi?

65
xyz

Mentre tutti qui hanno già suggerito come creare il tuo web crawler, ecco come Google classifica le pagine.

Google assegna a ciascuna pagina una classifica in base al numero di link di richiamata (quanti link su altri siti web puntano a un sito Web/pagina specifici). Questo è chiamato punteggio di rilevanza. Questo si basa sul fatto che se una pagina ha molte altre pagine ad essa collegate, è probabilmente una pagina importante.

Ogni sito/pagina viene visualizzato come un nodo in un grafico. I collegamenti ad altre pagine sono bordi diretti. Un grado di un vertice è definito come il numero di bordi in entrata. I nodi con un numero maggiore di bordi in entrata sono classificati più in alto.

Ecco come viene determinato il PageRank. Supponiamo che la pagina Pj abbia collegamenti Lj. Se uno di questi collegamenti è sulla pagina Pi, Pj trasmetterà 1/Lj della sua importanza a Pi. La classifica di importanza di Pi è quindi la somma di tutti i contributi fatti dalle pagine che vi collegano. Quindi se indichiamo il set di pagine che collegano a Pi da Bi, allora abbiamo questa formula:

Importance(Pi)= sum( Importance(Pj)/Lj ) for all links from Pi to Bi

I ranghi sono posizionati in una matrice chiamata matrice hyperlink: H [i, j]

Una riga in questa matrice è 0 o 1/Lj se esiste un collegamento da Pi a Bi. Un'altra proprietà di questa matrice è che se sommiamo tutte le righe in una colonna otteniamo 1. 

Ora abbiamo bisogno di moltiplicare questa matrice con un vettore Eigen, chiamato I (con valore di autovalore 1) tale che:

I = H*I

Ora iniziamo a ripetere: IH, I IH, I II H .... I ^ k * H finché la soluzione non converge. cioè otteniamo praticamente gli stessi numeri nella matrice nel passaggio k e k + 1.

Ora tutto ciò che rimane nel vettore I è l'importanza di ogni pagina.

Per un semplice esempio di compiti a casa vedi http://www.math.cornell.edu/~mec/Winter2009/RalucaRemus/Lecture3/lecture3.html

Per risolvere il problema duplicato nella domanda dell'intervista, esegui un checksum sull'intera pagina e utilizza quello o una bash del checksum come chiave in una mappa per tenere traccia delle pagine visitate.

7
Adrian

Dipende da quanto profonda doveva essere la loro domanda. Se stessero solo cercando di evitare di seguire gli stessi link avanti e indietro, l'hashing dell'URL sarebbe sufficiente.

Che dire del contenuto che ha letteralmente migliaia di URL che portano allo stesso contenuto? Come un parametro QueryString che non influisce su nulla, ma può avere un numero infinito di iterazioni. Suppongo che potresti anche cancellare il contenuto della pagina e confrontare gli URL per vedere se sono simili a catturare contenuti identificati da più URL. Vedi ad esempio, Bot Traps menzionato nel post di @Lirik.

1
mellamokb

Il problema qui non è quello di eseguire la scansione di URL duplicati, che vengono risolti da un indice utilizzando un hash ottenuto dagli URL. Il problema è eseguire la scansione del CONTENUTO DUPLICATO. Ogni URL di una "Trappola crawler" è diverso (anno, giorno, SessionID ...).

Non esiste una soluzione "perfetta" ... ma puoi utilizzare alcune di queste strategie:

• Mantieni un campo di livello in cui l'url si trova all'interno del sito web. Per ogni ciclo di ottenere gli URL da una pagina, aumentare il livello. Sarà come un albero. Puoi smettere di eseguire la scansione a un determinato livello, ad esempio 10 (penso che Google lo usi). 

• Puoi provare a creare un tipo di HASH che può essere paragonato per trovare documenti simili, dato che non puoi confrontare con ciascun documento nel tuo database. Ci sono i SimHash di google, ma non sono riuscito a trovare alcuna implementazione da usare. Quindi ho creato il mio. Il mio hash conta i caratteri di bassa e alta frequenza all'interno del codice html e genera un hash da 20 byte, che viene confrontato con una piccola cache dell'ultima pagina sottoposta a scansione all'interno di un AVLTree con una ricerca NearNeighbors con una certa tolleranza (circa 2). Non puoi usare alcun riferimento alle posizioni dei personaggi in questo hash. Dopo aver "riconosciuto" la trappola, è possibile registrare il modello di URL del contenuto duplicato e iniziare a ignorare anche le pagine.

• Come google, puoi creare una classifica per ogni sito web e "fidarti" di più in uno rispetto ad altri.

0
lexmooze

Dovresti avere una sorta di tabella hash per archiviare i risultati, dovresti solo controllarlo prima di caricare ogni pagina. 

0
chuchu

Ho anche richiesto di utilizzare il crawler e non riesco a trovarne uno adeguato per i miei requisiti, quindi ho sviluppato una libreria di crawler di base per implementare semplici requisiti. Ma abilitare a soddisfare quasi tutti i principi del crawler . È possibile controllare DotnetCrawler github repo che implementa i moduli di Downloader-Processor-Pipeline con l'implementazione predefinita utilizzando Entity Framework Core per salvare i dati in Sql Server.

https://github.com/mehmetozkaya/DotnetCrawler

0
Mehmet Özkaya

Il crawler mantiene un pool di URL che contiene tutti gli URL da sottoporre a scansione. Per evitare il "ciclo infinito", l'idea di base è controllare l'esistenza di ogni URL prima di aggiungerlo al pool.

Tuttavia, questo non è facile da implementare quando il sistema è ridimensionato a un certo livello. L'approccio ingenuo è mantenere tutti gli URL in un hashset e controllare l'esistenza di ogni nuovo URL. Questo non funzionerà quando ci sono troppi URL per adattarsi alla memoria.

Ci sono un paio di soluzioni qui. Ad esempio, invece di archiviare tutti gli URL in memoria, dovremmo tenerli sul disco. Per risparmiare spazio, è necessario utilizzare l'URL hash anziché l'URL non elaborato. Vale anche la pena notare che dovremmo mantenere la forma canonica di URL piuttosto che quella originale. Quindi se l'URL viene abbreviato da servizi come bit.ly, è meglio ottenere l'URL finale. Per accelerare il processo di verifica, è possibile creare un livello di cache. Oppure puoi vederlo come un sistema di cache distribuita, che è un argomento separato.

Il post Build a Web Crawler ha un'analisi dettagliata di questo problema. 

0
Mark

Il crawler web è un programma per computer che ha utilizzato per raccogliere/eseguire la scansione dei seguenti valori chiave (collegamenti HREF, collegamenti immagine, metadati .etc) dall'URL del sito Web specificato. È progettato come intelligente per seguire diversi collegamenti HREF che sono già stati recuperati dall'URL precedente, quindi in questo modo, Crawler può passare da un sito Web ad altri siti Web. Solitamente, ha chiamato come spider Web o Web Bot. Questo meccanismo funge sempre da dorsale del motore di ricerca Web.

Si prega di trovare il codice sorgente dal mio blog di tecnologia - http://www.algonuts.info/how-to-built-a-simple-web-crawler-in-php.html

<?php
class webCrawler
{
    public $siteURL;
    public $error;

    function __construct()
    {
        $this->siteURL = "";
        $this->error = "";
    }

    function parser()   
    {
        global $hrefTag,$hrefTagCountStart,$hrefTagCountFinal,$hrefTagLengthStart,$hrefTagLengthFinal,$hrefTagPointer;
        global $imgTag,$imgTagCountStart,$imgTagCountFinal,$imgTagLengthStart,$imgTagLengthFinal,$imgTagPointer;
        global $Url_Extensions,$Document_Extensions,$Image_Extensions,$crawlOptions;

        $dotCount = 0;
        $slashCount = 0;
        $singleSlashCount = 0;
        $doubleSlashCount = 0;
        $parentDirectoryCount = 0;

        $linkBuffer = array();

        if(($url = trim($this->siteURL)) != "")
        {
            $crawlURL = rtrim($url,"/");
            if(($directoryURL = dirname($crawlURL)) == "http:")
            {   $directoryURL = $crawlURL;  }
            $urlParser = preg_split("/\//",$crawlURL);

            //-- Curl Start --
            $curlObject = curl_init($crawlURL);
            curl_setopt_array($curlObject,$crawlOptions);
            $webPageContent = curl_exec($curlObject);
            $errorNumber = curl_errno($curlObject);
            curl_close($curlObject);
            //-- Curl End --

            if($errorNumber == 0)
            {
                $webPageCounter = 0;
                $webPageLength = strlen($webPageContent);
                while($webPageCounter < $webPageLength)
                {
                    $character = $webPageContent[$webPageCounter];
                    if($character == "")
                    {   
                        $webPageCounter++;  
                        continue;
                    }
                    $character = strtolower($character);
                    //-- Href Filter Start --
                    if($hrefTagPointer[$hrefTagLengthStart] == $character)
                    {
                        $hrefTagLengthStart++;
                        if($hrefTagLengthStart == $hrefTagLengthFinal)
                        {
                            $hrefTagCountStart++;
                            if($hrefTagCountStart == $hrefTagCountFinal)
                            {
                                if($hrefURL != "")
                                {
                                    if($parentDirectoryCount >= 1 || $singleSlashCount >= 1 || $doubleSlashCount >= 1)
                                    {
                                        if($doubleSlashCount >= 1)
                                        {   $hrefURL = "http://".$hrefURL;  }
                                        else if($parentDirectoryCount >= 1)
                                        {
                                            $tempData = 0;
                                            $tempString = "";
                                            $tempTotal = count($urlParser) - $parentDirectoryCount;
                                            while($tempData < $tempTotal)
                                            {
                                                $tempString .= $urlParser[$tempData]."/";
                                                $tempData++;
                                            }
                                            $hrefURL = $tempString."".$hrefURL;
                                        }
                                        else if($singleSlashCount >= 1)
                                        {   $hrefURL = $urlParser[0]."/".$urlParser[1]."/".$urlParser[2]."/".$hrefURL;  }
                                    }
                                    $Host = "";
                                    $hrefURL = urldecode($hrefURL);
                                    $hrefURL = rtrim($hrefURL,"/");
                                    if(filter_var($hrefURL,FILTER_VALIDATE_URL) == true)
                                    {   
                                        $dump = parse_url($hrefURL);
                                        if(isset($dump["Host"]))
                                        {   $Host = trim(strtolower($dump["Host"]));    }
                                    }
                                    else
                                    {
                                        $hrefURL = $directoryURL."/".$hrefURL;
                                        if(filter_var($hrefURL,FILTER_VALIDATE_URL) == true)
                                        {   
                                            $dump = parse_url($hrefURL);    
                                            if(isset($dump["Host"]))
                                            {   $Host = trim(strtolower($dump["Host"]));    }
                                        }
                                    }
                                    if($Host != "")
                                    {
                                        $extension = pathinfo($hrefURL,PATHINFO_EXTENSION);
                                        if($extension != "")
                                        {
                                            $tempBuffer ="";
                                            $extensionlength = strlen($extension);
                                            for($tempData = 0; $tempData < $extensionlength; $tempData++)
                                            {
                                                if($extension[$tempData] != "?")
                                                {   
                                                    $tempBuffer = $tempBuffer.$extension[$tempData];
                                                    continue;
                                                }
                                                else
                                                {
                                                    $extension = trim($tempBuffer);
                                                    break;
                                                }
                                            }
                                            if(in_array($extension,$Url_Extensions))
                                            {   $type = "domain";   }
                                            else if(in_array($extension,$Image_Extensions))
                                            {   $type = "image";    }
                                            else if(in_array($extension,$Document_Extensions))
                                            {   $type = "document"; }
                                            else
                                            {   $type = "unknown";  }
                                        }
                                        else
                                        {   $type = "domain";   }

                                        if($hrefURL != "")
                                        {
                                            if($type == "domain" && !in_array($hrefURL,$this->linkBuffer["domain"]))
                                            {   $this->linkBuffer["domain"][] = $hrefURL;   }
                                            if($type == "image" && !in_array($hrefURL,$this->linkBuffer["image"]))
                                            {   $this->linkBuffer["image"][] = $hrefURL;    }
                                            if($type == "document" && !in_array($hrefURL,$this->linkBuffer["document"]))
                                            {   $this->linkBuffer["document"][] = $hrefURL; }
                                            if($type == "unknown" && !in_array($hrefURL,$this->linkBuffer["unknown"]))
                                            {   $this->linkBuffer["unknown"][] = $hrefURL;  }
                                        }
                                    }
                                }
                                $hrefTagCountStart = 0;
                            }
                            if($hrefTagCountStart == 3)
                            {
                                $hrefURL = "";
                                $dotCount = 0;
                                $slashCount = 0;
                                $singleSlashCount = 0;
                                $doubleSlashCount = 0;
                                $parentDirectoryCount = 0;
                                $webPageCounter++;
                                while($webPageCounter < $webPageLength)
                                {
                                    $character = $webPageContent[$webPageCounter];
                                    if($character == "")
                                    {   
                                        $webPageCounter++;  
                                        continue;
                                    }
                                    if($character == "\"" || $character == "'")
                                    {
                                        $webPageCounter++;
                                        while($webPageCounter < $webPageLength)
                                        {
                                            $character = $webPageContent[$webPageCounter];
                                            if($character == "")
                                            {   
                                                $webPageCounter++;  
                                                continue;
                                            }
                                            if($character == "\"" || $character == "'" || $character == "#")
                                            {   
                                                $webPageCounter--;  
                                                break;  
                                            }
                                            else if($hrefURL != "")
                                            {   $hrefURL .= $character; }
                                            else if($character == "." || $character == "/")
                                            {
                                                if($character == ".")
                                                {
                                                    $dotCount++;
                                                    $slashCount = 0;
                                                }
                                                else if($character == "/")
                                                {
                                                    $slashCount++;
                                                    if($dotCount == 2 && $slashCount == 1)
                                                    $parentDirectoryCount++;
                                                    else if($dotCount == 0 && $slashCount == 1)
                                                    $singleSlashCount++;
                                                    else if($dotCount == 0 && $slashCount == 2)
                                                    $doubleSlashCount++;
                                                    $dotCount = 0;
                                                }
                                            }
                                            else
                                            {   $hrefURL .= $character; }
                                            $webPageCounter++;
                                        }
                                        break;
                                    }
                                    $webPageCounter++;
                                }
                            }
                            $hrefTagLengthStart = 0;
                            $hrefTagLengthFinal = strlen($hrefTag[$hrefTagCountStart]);
                            $hrefTagPointer =& $hrefTag[$hrefTagCountStart];
                        }
                    }
                    else
                    {   $hrefTagLengthStart = 0;    }
                    //-- Href Filter End --
                    //-- Image Filter Start --
                    if($imgTagPointer[$imgTagLengthStart] == $character)
                    {
                        $imgTagLengthStart++;
                        if($imgTagLengthStart == $imgTagLengthFinal)
                        {
                            $imgTagCountStart++;
                            if($imgTagCountStart == $imgTagCountFinal)
                            {
                                if($imgURL != "")
                                {
                                    if($parentDirectoryCount >= 1 || $singleSlashCount >= 1 || $doubleSlashCount >= 1)
                                    {
                                        if($doubleSlashCount >= 1)
                                        {   $imgURL = "http://".$imgURL;    }
                                        else if($parentDirectoryCount >= 1)
                                        {
                                            $tempData = 0;
                                            $tempString = "";
                                            $tempTotal = count($urlParser) - $parentDirectoryCount;
                                            while($tempData < $tempTotal)
                                            {
                                                $tempString .= $urlParser[$tempData]."/";
                                                $tempData++;
                                            }
                                            $imgURL = $tempString."".$imgURL;
                                        }
                                        else if($singleSlashCount >= 1)
                                        {   $imgURL = $urlParser[0]."/".$urlParser[1]."/".$urlParser[2]."/".$imgURL;    }
                                    }
                                    $Host = "";
                                    $imgURL = urldecode($imgURL);
                                    $imgURL = rtrim($imgURL,"/");
                                    if(filter_var($imgURL,FILTER_VALIDATE_URL) == true)
                                    {   
                                        $dump = parse_url($imgURL); 
                                        $Host = trim(strtolower($dump["Host"]));
                                    }
                                    else
                                    {
                                        $imgURL = $directoryURL."/".$imgURL;
                                        if(filter_var($imgURL,FILTER_VALIDATE_URL) == true)
                                        {   
                                            $dump = parse_url($imgURL); 
                                            $Host = trim(strtolower($dump["Host"]));
                                        }   
                                    }
                                    if($Host != "")
                                    {
                                        $extension = pathinfo($imgURL,PATHINFO_EXTENSION);
                                        if($extension != "")
                                        {
                                            $tempBuffer ="";
                                            $extensionlength = strlen($extension);
                                            for($tempData = 0; $tempData < $extensionlength; $tempData++)
                                            {
                                                if($extension[$tempData] != "?")
                                                {   
                                                    $tempBuffer = $tempBuffer.$extension[$tempData];
                                                    continue;
                                                }
                                                else
                                                {
                                                    $extension = trim($tempBuffer);
                                                    break;
                                                }
                                            }
                                            if(in_array($extension,$Url_Extensions))
                                            {   $type = "domain";   }
                                            else if(in_array($extension,$Image_Extensions))
                                            {   $type = "image";    }
                                            else if(in_array($extension,$Document_Extensions))
                                            {   $type = "document"; }
                                            else
                                            {   $type = "unknown";  }
                                        }
                                        else
                                        {   $type = "domain";   }

                                        if($imgURL != "")
                                        {
                                            if($type == "domain" && !in_array($imgURL,$this->linkBuffer["domain"]))
                                            {   $this->linkBuffer["domain"][] = $imgURL;    }
                                            if($type == "image" && !in_array($imgURL,$this->linkBuffer["image"]))
                                            {   $this->linkBuffer["image"][] = $imgURL; }
                                            if($type == "document" && !in_array($imgURL,$this->linkBuffer["document"]))
                                            {   $this->linkBuffer["document"][] = $imgURL;  }
                                            if($type == "unknown" && !in_array($imgURL,$this->linkBuffer["unknown"]))
                                            {   $this->linkBuffer["unknown"][] = $imgURL;   }
                                        }
                                    }
                                }
                                $imgTagCountStart = 0;
                            }
                            if($imgTagCountStart == 3)
                            {
                                $imgURL = "";
                                $dotCount = 0;
                                $slashCount = 0;
                                $singleSlashCount = 0;
                                $doubleSlashCount = 0;
                                $parentDirectoryCount = 0;
                                $webPageCounter++;
                                while($webPageCounter < $webPageLength)
                                {
                                    $character = $webPageContent[$webPageCounter];
                                    if($character == "")
                                    {   
                                        $webPageCounter++;  
                                        continue;
                                    }
                                    if($character == "\"" || $character == "'")
                                    {
                                        $webPageCounter++;
                                        while($webPageCounter < $webPageLength)
                                        {
                                            $character = $webPageContent[$webPageCounter];
                                            if($character == "")
                                            {   
                                                $webPageCounter++;  
                                                continue;
                                            }
                                            if($character == "\"" || $character == "'" || $character == "#")
                                            {   
                                                $webPageCounter--;  
                                                break;  
                                            }
                                            else if($imgURL != "")
                                            {   $imgURL .= $character;  }
                                            else if($character == "." || $character == "/")
                                            {
                                                if($character == ".")
                                                {
                                                    $dotCount++;
                                                    $slashCount = 0;
                                                }
                                                else if($character == "/")
                                                {
                                                    $slashCount++;
                                                    if($dotCount == 2 && $slashCount == 1)
                                                    $parentDirectoryCount++;
                                                    else if($dotCount == 0 && $slashCount == 1)
                                                    $singleSlashCount++;
                                                    else if($dotCount == 0 && $slashCount == 2)
                                                    $doubleSlashCount++;
                                                    $dotCount = 0;
                                                }
                                            }
                                            else
                                            {   $imgURL .= $character;  }
                                            $webPageCounter++;
                                        }
                                        break;
                                    }
                                    $webPageCounter++;
                                }
                            }
                            $imgTagLengthStart = 0;
                            $imgTagLengthFinal = strlen($imgTag[$imgTagCountStart]);
                            $imgTagPointer =& $imgTag[$imgTagCountStart];
                        }
                    }
                    else
                    {   $imgTagLengthStart = 0; }
                    //-- Image Filter End --
                    $webPageCounter++;
                }
            }
            else
            {   $this->error = "Unable to proceed, permission denied";  }
        }
        else
        {   $this->error = "Please enter url";  }

        if($this->error != "")
        {   $this->linkBuffer["error"] = $this->error;  }

        return $this->linkBuffer;
    }   
}
?>
0