Creare un crawler in Java e crwaler4j

Mattepuffo's logo
Creare un crawler in Java e crwaler4j

Creare un crawler in Java e crwaler4j

Cominciamo con la descrizione di Wikipedia su cosa sia un crawler:

Un crawler (detto anche web crawler, spider o robot), è un software che analizza i contenuti di una rete (o di un database) in un modo metodico e automatizzato, in genere per conto di un motore di ricerca. Nello specifico, un crawler è un tipo di bot (programma o script che automatizza delle operazioni), che solitamente acquisisce una copia testuale di tutti i documenti presenti in una o più pagine web creando un indice che ne permetta, successivamente, la ricerca e la visualizzazione.

Un uso estremamente comune dei crawler viene effettuato sul Web; esso si basa su una lista di URL da visitare fornita dal motore di ricerca (il quale, inizialmente, si basa a sua volta sugli indirizzi suggeriti dagli utenti o su una lista precompilata dai programmatori stessi). Durante l'analisi di una URL, identifica tutti i collegamenti ipertestuali presenti nel documento e li aggiunge alla lista di URL da visitare. Il processo può essere concluso manualmente o dopo che un determinato numero di collegamenti è stato seguito.

Inoltre i crawler attivi su Internet hanno la facoltà di essere indirizzati da quanto indicato nel file "robots.txt" posto nella root del sito. All'interno di questo file, è possibile indicare quali pagine non dovrebbero essere analizzate. Il crawler ha la facoltà di seguire i consigli, ma non l'obbligo.

Partendo da questa definizione, vediamo come creare un semplice crawler in Java, usando la libreria apposita crawler4j!

Questa libreria, tra le altre cose, è anche robots.txt friendly.

Se usate Maven:

    <dependencies>
        <dependency>
            <groupId>edu.uci.ics</groupId>
            <artifactId>crawler4j</artifactId>
            <version>4.2</version>
        </dependency>
    </dependencies>

Per mia semplicità, visto che stavo facendo diversi test, ho creato una classe con alcuni campi statici da usare in tutto il progetto:

public class StaticConfig {
    public static String URL = "https://www.mattepuffo.com/blog";
    public static String MOZ_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0";
    public static String STORAGE_FOLDER_NIX = "/home/utente/MPC";
}

Ho anche impostato lo user agent, sennò non funzionava su nessun sito; e la cartella dove verranno salvati i dati; sentitevi liberi di cambiare i parametri ovviamente.

Detto ciò vediamo la classe che si occupa del lavoro sporco:

import edu.uci.ics.crawler4j.crawler.Page;
import edu.uci.ics.crawler4j.crawler.WebCrawler;
import edu.uci.ics.crawler4j.parser.HtmlParseData;
import edu.uci.ics.crawler4j.url.WebURL;
import org.apache.http.Header;
import java.util.Set;
import java.util.regex.Pattern;

public class MPCrawler extends WebCrawler {

    private final static Pattern FILTERS = Pattern.compile(".*(\.(css|js|gif|jpg|png|mp3|mp3|zip|gz))$");

    @Override
    public boolean shouldVisit(Page referringPage, WebURL url) {
        String href = url.getURL().toLowerCase();
        return !FILTERS.matcher(href).matches() && href.startsWith(StaticConfig.URL);
    }

    @Override
    public void visit(Page page) {
        int docid = page.getWebURL().getDocid();
        String url = page.getWebURL().getURL();
        String domain = page.getWebURL().getDomain();
        String path = page.getWebURL().getPath();
        String subDomain = page.getWebURL().getSubDomain();
        String parentUrl = page.getWebURL().getParentUrl();
        String anchor = page.getWebURL().getAnchor();

        System.out.println("Docid: " + docid);
        System.out.println("URL: " + url);
        System.out.println("Domain: ''" + domain);
        System.out.println("Sub-domain: ''" + subDomain);
        System.out.println("Path: ''" + path);
        System.out.println("Parent page: " + parentUrl);
        System.out.println("Anchor text: " + anchor);

        if (page.getParseData() instanceof HtmlParseData) {
            HtmlParseData htmlParseData = (HtmlParseData) page.getParseData();
            String text = htmlParseData.getText();
            String html = htmlParseData.getHtml();
            Set<WebURL> links = htmlParseData.getOutgoingUrls();

            System.out.println("Text length: " + text.length());
            System.out.println("Html length: " + html.length());
            System.out.println("Number of outgoing links: " + links.size());
        }

        Header[] responseHeaders = page.getFetchResponseHeaders();
        if (responseHeaders != null) {
            System.out.println("Response headers:");
            for (Header header : responseHeaders) {
                System.out.println(header.getName() + " - " + header.getValue());
            }
        }
        System.out.println("=============");
    }
}

In shoudlVisit, impostiamo che devono essere visitate solo le pagine del dominio impostato prima, e non devono essere tenuti in considerazione tutti i files impostati nel filtro.

Poi lanciamo tutto così:

import edu.uci.ics.crawler4j.crawler.CrawlConfig;
import edu.uci.ics.crawler4j.crawler.CrawlController;
import edu.uci.ics.crawler4j.fetcher.PageFetcher;
import edu.uci.ics.crawler4j.robotstxt.RobotstxtConfig;
import edu.uci.ics.crawler4j.robotstxt.RobotstxtServer;

public class Main {

    public static void main(String[] args) {
        try {
            int numberOfCrawlers = 8;
            CrawlConfig config = new CrawlConfig();
            config.setCrawlStorageFolder(StaticConfig.STORAGE_FOLDER_NIX);
            config.setIncludeHttpsPages(true);
            config.setIncludeBinaryContentInCrawling(false);
            config.setUserAgentString(StaticConfig.MOZ_USER_AGENT);

            PageFetcher pageFetcher = new PageFetcher(config);
            RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
            RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher);

            CrawlController controller = new CrawlController(config, pageFetcher, robotstxtServer);
            controller.addSeed(StaticConfig.URL);
            CrawlController.WebCrawlerFactory<MPCrawler> factory = () -> new MPCrawler();
            controller.start(factory, numberOfCrawlers);
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
}

Qui sotto un esempio di risposta:

Docid: 104
URL: https://www.mattepuffo.com/blog/categoria/21-kotlin.html
Domain: ''mattepuffo.com
Sub-domain: ''www
Path: ''/blog/categoria/21-kotlin.html
Parent page: https://www.mattepuffo.com/blog/
Anchor text: Kotlin
Text length: 32556
Html length: 87216
Number of outgoing links: 146
Response headers:
Date - Wed, 12 Jun 2019 08:52:22 GMT
Server - Apache
Expires - Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control - no-store, no-cache, must-revalidate
Pragma - no-cache
Access-Control-Allow-Origin - *
Keep-Alive - timeout=15, max=96
Connection - Keep-Alive
Transfer-Encoding - chunked
Content-Type - text/html; charset=UTF-8
=============

Enjoy!


Condividi

Commentami!