jsoup - Exemple de robot d’indexation Web de base

jsoup - Exemple basique de robot d'indexation Web

web-crawler-spider-logo

UnWeb Crawler est un programme qui navigue sur le Web et trouve des pages nouvelles ou mises à jour pour l'indexation. Le Crawler commence par des sites Web d'amorçage ou un large éventail d'URL populaires (également appeléesfrontier) et recherche en profondeur et en largeur les liens hypertexte à extraire.

Un robot d'exploration Web doit être gentil et robuste. La gentillesse d'un Crawler signifie qu'il respecte les règles définies par le robots.txt et évite de visiter trop souvent un site Web. La robustesse fait référence à la capacité d'éviter les pièges à araignées et autres comportements malveillants. D'autres bons attributs pour un Web Crawler sont la distributivité entre plusieurs machines distribuées, l'extensibilité, la continuité et la capacité de prioriser en fonction de la qualité de la page.

1. Étapes pour créer un robot d'indexation Web

Les étapes de base pour écrire un robot d'indexation Web sont les suivantes:

  1. Choisissez une URL de la frontière

  2. Récupérer le code HTML

  3. Analyser le code HTML pour extraire les liens vers d'autres URL

  4. Vérifiez si vous avez déjà exploré les URL et / ou si vous avez déjà vu le même contenu

    • Sinon, ajoutez-le à l'index

Pour chaque URL extraite

  • Confirmer qu'il accepte d'être vérifié (robots.txt, fréquence d'exploration)

À vrai dire, développer et maintenir un Web Crawler sur toutes les pages d'Internet est… Difficile, voire impossible, étant donné qu'il y a plus de1 billion websitesen ligne en ce moment. Si vous lisez cet article, il est probable que vous ne cherchiez pas un guide pour créer un Web Crawler mais un Web Scraper. Pourquoi l'article s'appelle-t-il alors "Basic Web Crawler"? Eh bien… Parce que c'est accrocheur… Vraiment! Peu de gens connaissent la différence entre les robots d'exploration et les grattoirs, nous avons donc tous tendance à utiliser le mot "analyse" pour tout, même pour le grattage de données hors ligne. De plus, parce que pour construire un Web Scraper, vous avez également besoin d'un agent d'analyse. Et enfin, parce que cet article vise à informer et à fournir un exemple viable.

2. Le squelette d'un robot

Pour l'analyse HTML, nous utiliseronsjsoup. Les exemples ci-dessous ont été développés à l'aide de la version 1.10.2 de jsoup.

pom.xml

        
            org.jsoup
            jsoup
            1.10.2
        

Commençons donc par le code de base d'un robot d'exploration Web.

BasicWebCrawler.java

package com.example.basicwebcrawler;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.util.HashSet;

public class BasicWebCrawler {

    private HashSet links;

    public BasicWebCrawler() {
        links = new HashSet();
    }

    public void getPageLinks(String URL) {
        //4. Check if you have already crawled the URLs
        //(we are intentionally not checking for duplicate content in this example)
        if (!links.contains(URL)) {
            try {
                //4. (i) If not add it to the index
                if (links.add(URL)) {
                    System.out.println(URL);
                }

                //2. Fetch the HTML code
                Document document = Jsoup.connect(URL).get();
                //3. Parse the HTML to extract links to other URLs
                Elements linksOnPage = document.select("a[href]");

                //5. For each extracted URL... go back to Step 4.
                for (Element page : linksOnPage) {
                    getPageLinks(page.attr("abs:href"));
                }
            } catch (IOException e) {
                System.err.println("For '" + URL + "': " + e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        //1. Pick a URL from the frontier
        new BasicWebCrawler().getPageLinks("http://www.example.com/");
    }

}

Note
Ne laissez pas ce code s'exécuter trop longtemps. Cela peut prendre des heures sans se terminer.

Exemple de sortie:

Comme nous l'avons mentionné précédemment, un robot d'exploration Web recherche en largeur et en profondeur les liens. Si nous imaginons les liens sur un site Web dans une structure arborescente, le nœud racine ou niveau zéro serait le lien par lequel nous commençons, le niveau suivant serait tous les liens que nous avons trouvés au niveau zéro et ainsi de suite.

3. Prise en compte de la profondeur d'exploration

Nous allons modifier l'exemple précédent pour définir la profondeur d'extraction du lien. Notez que la seule vraie différence entre cet exemple et le précédent est que la méthode récursivegetPageLinks() a un argument entier qui représente la profondeur du lien qui est également ajoutée en tant que condition dans l'instructionif...else.

WebCrawlerWithDepth.java

package com.example.depthwebcrawler;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.util.HashSet;

public class WebCrawlerWithDepth {
    private static final int MAX_DEPTH = 2;
    private HashSet links;

    public WebCrawlerWithDepth() {
        links = new HashSet<>();
    }

    public void getPageLinks(String URL, int depth) {
        if ((!links.contains(URL) && (depth < MAX_DEPTH))) {
            System.out.println(">> Depth: " + depth + " [" + URL + "]");
            try {
                links.add(URL);

                Document document = Jsoup.connect(URL).get();
                Elements linksOnPage = document.select("a[href]");

                depth++;
                for (Element page : linksOnPage) {
                    getPageLinks(page.attr("abs:href"), depth);
                }
            } catch (IOException e) {
                System.err.println("For '" + URL + "': " + e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        new WebCrawlerWithDepth().getPageLinks("http://www.example.com/", 0);
    }
}

Note
N'hésitez pas à exécuter le code ci-dessus. Cela n'a pris que quelques minutes sur mon ordinateur portable avec une profondeur réglée sur 2. Veuillez garder à l'esprit que plus la profondeur est élevée, plus il faudra de temps pour terminer.

Exemple de sortie:

...
>> Depth: 1 [https://docs.gradle.org/current/userguide/userguide.html]
>> Depth: 1 [http://hibernate.org/orm/]
>> Depth: 1 [https://jax-ws.java.net/]
>> Depth: 1 [http://tomcat.apache.org/tomcat-8.0-doc/index.html]
>> Depth: 1 [http://www.javacodegeeks.com/]
For 'http://www.javacodegeeks.com/': HTTP error fetching URL
>> Depth: 1 [http://beust.com/weblog/]
>> Depth: 1 [https://dzone.com]
>> Depth: 1 [https://wordpress.org/]
>> Depth: 1 [http://www.liquidweb.com/?RID=example]
>> Depth: 1 [http://www.example.com/privacy-policy/]

4. Scraping de données vs. Analyse des données

Jusqu'ici tout va bien pour une approche théorique en la matière. Le fait est que vous ne construirez presque jamais un robot générique, et si vous en voulez un "réel", vous devez utiliser des outils qui existent déjà. La plupart de ce que fait le développeur moyen est une extraction d'informations spécifiques à partir de sites Web spécifiques et même si cela comprend la création d'un robot d'exploration Web, il s'agit en fait de Web Scraping.

Il y a un très bon article d'Arpan Jha pour PromptCloud surData Scraping vs. Data Crawling qui m'a personnellement beaucoup aidé à comprendre cette distinction et je suggère de le lire.

Pour le résumer avec un tableau extrait de cet article:

Grattage des données Analyse des données

Implique l'extraction de données à partir de diverses sources, y compris le Web

Fait référence au téléchargement de pages à partir du Web

Peut être fait à n'importe quelle échelle

Principalement fait à grande échelle

La déduplication ne fait pas nécessairement partie

La déduplication est une partie essentielle

Nécessite un agent d'exploration et un analyseur syntaxique

Nécessite uniquement un agent d'exploration

Il est temps de sortir de la théorie et de devenir un exemple viable, comme promis dans l'intro. Imaginons un scénario dans lequel nous souhaitons obtenir toutes les URL des articles relatifs à Java 8 à partir d’exemple.com. Notre objectif est de récupérer ces informations dans les plus brefs délais et d'éviter ainsi de parcourir tout le site Web. En outre, cette approche ne gaspillera pas seulement les ressources du serveur, mais aussi notre temps.

5. Étude de cas - Extrayez tous les articles pour «Java 8» sur example.com

5.1 First thing we should do is look at the code of the website. En jetant un coup d'œil rapide à example.com, nous pouvons facilement remarquer la pagination sur la première page et qu'elle suit un modèle/page/xx pour chaque page.

jsoup-web-crawler-example-1

Cela nous amène à réaliser que les informations que nous recherchons sont facilement accessibles en récupérant tous les liens qui incluent/page/. Donc, au lieu de parcourir tout le site Web, nous limiterons notre recherche en utilisantdocument.select("a[href^="http://www.example.com/page/"]"). Avec cecss selector, nous ne collectons que les liens commençant parhttp://example.com/page/.

5.2 Next thing we notice is that the titles of the articles -which is what we want- are wrapped in <h2></h2> and <a href=""></a> tags.

jsoup-web-crawler-example-2

Donc, pour extraire les titres des articles, nous accèderons à ces informations spécifiques en utilisant uncss selector qui restreint notre méthodeselect à ces informations exactes:document.select("h2 a[href^="http://www.example.com/"]");

5.3 Finally, we will only keep the links in which the title contains ‘Java 8’ and save them to a file.

Extractor.java

package com.example.extractor;

package com.example;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class Extractor {
    private HashSet links;
    private List> articles;

    public Extractor() {
        links = new HashSet<>();
        articles = new ArrayList<>();
    }

    //Find all URLs that start with "http://www.example.com/page/" and add them to the HashSet
    public void getPageLinks(String URL) {
        if (!links.contains(URL)) {
            try {
                Document document = Jsoup.connect(URL).get();
                Elements otherLinks = document.select("a[href^=\"http://www.example.com/page/\"]");

                for (Element page : otherLinks) {
                    if (links.add(URL)) {
                        //Remove the comment from the line below if you want to see it running on your editor
                        System.out.println(URL);
                    }
                    getPageLinks(page.attr("abs:href"));
                }
            } catch (IOException e) {
                System.err.println(e.getMessage());
            }
        }
    }

    //Connect to each link saved in the article and find all the articles in the page
    public void getArticles() {
        links.forEach(x -> {
            Document document;
            try {
                document = Jsoup.connect(x).get();
                Elements articleLinks = document.select("h2 a[href^=\"http://www.example.com/\"]");
                for (Element article : articleLinks) {
                    //Only retrieve the titles of the articles that contain Java 8
                    if (article.text().matches("^.*?(Java 8|java 8|JAVA 8).*$")) {
                        //Remove the comment from the line below if you want to see it running on your editor,
                        //or wait for the File at the end of the execution
                        //System.out.println(article.attr("abs:href"));

                        ArrayList temporary = new ArrayList<>();
                        temporary.add(article.text()); //The title of the article
                        temporary.add(article.attr("abs:href")); //The URL of the article
                        articles.add(temporary);
                    }
                }
            } catch (IOException e) {
                System.err.println(e.getMessage());
            }
        });
    }

    public void writeToFile(String filename) {
        FileWriter writer;
        try {
            writer = new FileWriter(filename);
            articles.forEach(a -> {
                try {
                    String temp = "- Title: " + a.get(0) + " (link: " + a.get(1) + ")\n";
                    //display to console
                    System.out.println(temp);
                    //save to file
                    writer.write(temp);
                } catch (IOException e) {
                    System.err.println(e.getMessage());
                }
            });
            writer.close();
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }
    }

    public static void main(String[] args) {
        Extractor bwc = new Extractor();
        bwc.getPageLinks("http://www.example.com");
        bwc.getArticles();
        bwc.writeToFile("Java 8 Articles");
    }
}

Sortie:

jsoup-web-crawler-example-3