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!
java crawler crawler4j
Commentami!