Aller au contenu
Java 6 min de lecture

Web scraping avec Java et Jsoup : guide pratique

Extraire des données web avec Java et Jsoup : sélecteurs CSS, pagination, headers HTTP, gestion des erreurs, rate limiting et respect des conditions d'utilisation.

JavaWeb scrapingJsoupAutomatisationData

Jsoup est la bibliothèque Java de référence pour le web scraping HTML. Simple pour les cas basiques, suffisamment flexible pour les cas complexes. Ce guide couvre les patterns essentiels pour extraire des données de manière fiable.

Dépendance Maven

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.17.2</version>
</dependency>

Extraction basique

public class ScraperBasique {

    public static void main(String[] args) throws IOException {
        // Connexion avec headers réalistes
        Document doc = Jsoup.connect("https://exemple.com/produits")
            .userAgent("Mozilla/5.0 (compatible; MonBot/1.0)")
            .header("Accept-Language", "fr-FR,fr;q=0.9")
            .timeout(10_000)
            .get();

        // Sélecteurs CSS — même syntaxe que jQuery
        Elements produits = doc.select(".product-card");

        for (Element produit : produits) {
            String nom = produit.select("h2.product-title").text();
            String prix = produit.select("[data-price]").attr("data-price");
            String lien = produit.select("a.product-link").attr("abs:href");
            // abs:href résout les URLs relatives en absolues automatiquement

            System.out.printf("Produit: %s | Prix: %s | URL: %s%n", nom, prix, lien);
        }
    }
}

Gérer la pagination

La plupart des sites ont plusieurs pages. Deux patterns courants : lien “Page suivante” ou numérotation explicite.

public List<ProduitDto> scrapeAllPages(String baseUrl) throws IOException {
    List<ProduitDto> tous = new ArrayList<>();
    String url = baseUrl;

    while (url != null) {
        Document doc = fetchWithRetry(url);

        // Extraire les données de la page courante
        tous.addAll(extractProduits(doc));

        // Trouver le lien vers la page suivante
        Element nextLink = doc.selectFirst("a[rel=next], .pagination .next a");
        url = nextLink != null ? nextLink.attr("abs:href") : null;

        // Rate limiting : pause entre les requêtes
        if (url != null) {
            Thread.sleep(1000 + (long)(Math.random() * 500));
        }
    }

    return tous;
}

Robustesse avec retry et backoff

private Document fetchWithRetry(String url) throws IOException {
    int maxRetries = 3;
    int delayMs = 2000;

    for (int attempt = 1; attempt <= maxRetries; attempt++) {
        try {
            return Jsoup.connect(url)
                .userAgent("Mozilla/5.0 (compatible; MonBot/1.0; +https://monsite.com/bot)")
                .timeout(15_000)
                .followRedirects(true)
                .maxBodySize(5 * 1024 * 1024) // 5 MB max
                .get();
        } catch (HttpStatusException e) {
            if (e.getStatusCode() == 429) {
                // Rate limited — attendre plus longtemps
                log.warn("Rate limited sur {}, attente {}ms", url, delayMs * attempt);
                Thread.sleep(delayMs * attempt);
            } else if (e.getStatusCode() >= 500) {
                log.warn("Erreur serveur {} sur {}", e.getStatusCode(), url);
                if (attempt == maxRetries) throw e;
                Thread.sleep(delayMs);
            } else {
                throw e; // 404, 403 etc. → pas de retry
            }
        } catch (SocketTimeoutException e) {
            log.warn("Timeout sur {}, tentative {}/{}", url, attempt, maxRetries);
            if (attempt == maxRetries) throw e;
            Thread.sleep(delayMs);
        }
    }
    throw new IOException("Échec après " + maxRetries + " tentatives : " + url);
}

Sélecteurs CSS essentiels

// Par classe CSS
doc.select(".ma-classe")

// Par attribut
doc.select("[data-id]")           // a cet attribut
doc.select("[data-type=produit]") // attribut avec valeur exacte
doc.select("[href^=https]")       // href commence par https

// Combinaisons
doc.select("article.product h2")    // h2 dans .product dans article
doc.select("ul.list > li")          // li enfant direct de ul.list
doc.select("li:first-child")        // premier élément
doc.select("li:not(.disabled)")     // li sans la classe disabled
doc.select("li:nth-child(3n)")      // un sur trois

// Texte et attributs
element.text()                    // texte visible (sans HTML)
element.html()                    // HTML interne
element.attr("href")              // valeur d'un attribut
element.attr("abs:href")          // URL absolue résolue
element.hasClass("active")        // boolean
element.ownText()                 // texte direct (sans les enfants)

Traiter du JavaScript rendu côté client

Jsoup ne peut pas exécuter JavaScript. Si le contenu est chargé dynamiquement (React, Angular, lazy loading), Jsoup récupère une page vide ou partielle.

Solutions :

1. Chercher l’API JSON : beaucoup de sites chargent leurs données via une API REST en arrière-plan. Ouvrez Chrome DevTools > Réseau > XHR/Fetch, rechargez la page et identifiez les appels API. Appelez directement l’API JSON — plus fiable que scraper le HTML.

2. Playwright ou Selenium : pour les sites vraiment incontournables qui nécessitent l’exécution JavaScript.

// Avec Playwright Java
try (Playwright playwright = Playwright.create()) {
    Browser browser = playwright.chromium().launch(
        new BrowserType.LaunchOptions().setHeadless(true));
    Page page = browser.newPage();
    page.navigate("https://exemple.com");
    page.waitForSelector(".product-card");

    String html = page.content();
    Document doc = Jsoup.parse(html);
    // Extraire normalement avec Jsoup
}

Exporter les données

// CSV avec OpenCSV
try (CSVWriter writer = new CSVWriter(new FileWriter("produits.csv"))) {
    writer.writeNext(new String[]{"Nom", "Prix", "URL"});
    for (ProduitDto p : produits) {
        writer.writeNext(new String[]{p.nom(), p.prix(), p.url()});
    }
}

// JSON avec Jackson
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(new File("produits.json"), produits);

Règles à respecter

robots.txt : vérifiez toujours https://site.com/robots.txt avant de scraper. Respectez les chemins interdits.

CGU : certains sites interdisent explicitement le scraping dans leurs conditions. Vérifiez avant de déployer en production.

Rate limiting : espacez vos requêtes (au minimum 1 seconde entre chaque). Un scraper agressif est bloqué ou provoque des surcharges.

User-Agent honnête : identifiez votre bot avec un User-Agent qui inclut un lien vers votre site de contact.

Conclusion

Jsoup est l’outil idéal pour le scraping HTML Java : API claire, sélecteurs CSS expressifs, gestion HTTP intégrée. La fiabilité vient de la gestion des erreurs et du rate limiting. Pour les sites JavaScript-heavy, Playwright est le complément naturel.

Amine MEGDICHE

Amine MEGDICHE

Développeur AEM & Java Full Stack — Freelance depuis 2013

Vous avez un projet sur ces sujets ?

Envoyez-moi un message pour qualifier votre besoin, vos contraintes techniques et le bon format d'intervention.

Cadrer mon besoin