Le problème : Trop de copier-coller

J’étudiais les maths et je me retrouvais constamment à copier des exercices de Bibmath.net dans des documents LaTeX. Le site a des tonnes d’exercices de qualité avec des solutions, mais le formatage manuel devenait pénible. Copier, coller, corriger le formatage, ajouter les commandes LaTeX, répéter. Il devait y avoir un meilleur moyen.

Site web source - Bibmath.net
Site web source - Bibmath.net

J’ai donc construit un scraper qui fait tout cela automatiquement. Rien de fantaisiste, juste un script Python qui récupère les exercices et produit des fichiers LaTeX propres prêts pour la compilation.

Scraping du contenu mathématique français

Le premier défi était de comprendre la structure HTML de Bibmath. Chaque page d’exercice a une mise en page spécifique avec des exercices, des indices (“indication”), et des solutions (“corrigé”). Voici ce que fait le scraper :

def fetch_chapitre(page):
    headers = {
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...',
        # ... more headers to avoid getting blocked
    }
    response = requests.get(page, headers=headers)
    soup = BeautifulSoup(response.text, 'lxml')
    article = soup.find('article', id='contenugauche')
    
    # Extract chapter title and create structure
    title = article.find('h1').get_text(strip=True)
    chapitre = Chapitre(title, url=response.url)
    
    # Process each section and exercise
    for element in article.find_all(recursive=False):
        if 'titrepartie' in element.get('class', []):
            # New section found
            part_title = element.get_text(strip=True)
            current_part = Part(part_title)
            chapitre.add_part(current_part)
        elif 'exo' in element.get('class', []):
            # Exercise found - extract all the data
            exercise = extract_exercise_data(element)
            current_part.add_exercise(exercise)

Conversion des maths HTML vers LaTeX

La partie la plus délicate était de gérer le contenu mathématique. Bibmath utilise un mélange de formatage HTML et de caractères spéciaux qui devaient être convertis en LaTeX approprié :

def parse(content):
    soup2 = BeautifulSoup(str(content), 'lxml')
    
    # Remove unwanted tags but keep the content
    for tag in soup2.find_all(['span', 'a', 'img']):
        tag.extract()
    
    # Handle numbered lists specially
    questions = []
    ol_list = soup2.find('ol', class_='enumeratechiffre')
    
    if ol_list:
        for i, li in enumerate(ol_list.find_all('li'), 1):
            if i == 1:
                questions.append("\\par")  # LaTeX paragraph break
            questions.append(f"{i}. {li.get_text().strip()}")
        list_text = "\n\n".join(questions)
        ol_list.replace_with(list_text)
    
    return soup2.get_text().strip()

Cela gère le cas courant où les exercices ont plusieurs sous-questions dans des listes numérotées. La sortie est propre en LaTeX.

Génération de fichiers LaTeX

Une fois les données d’exercice analysées, générer le LaTeX était simple. J’ai créé un système de modèles avec des fichiers d’en-tête et de pied de page :

class LatexFile:
    def generate_latex(self, chapitre):
        self.add_header()  # Standard LaTeX packages and setup
        
        # Add source attribution
        self.add_source(chapitre)
        
        # Generate exercises section
        self.add_content(f"\\title{{{chapitre.title}}}")
        for part in chapitre.parts:
            self.add_content(f"\\section{{{part.title}}}")
            for ex in part.exercises:
                self.add_exercise(ex)
        
        # Separate sections for hints and solutions
        self.add_pagebreak()
        for part in chapitre.parts:
            for ex in part.exercises:
                self.add_indication(ex)
                
        self.add_pagebreak()
        for part in chapitre.parts:
            for ex in part.exercises:
                self.add_answer(ex)
                
        self.add_footer()
        self.save()

Chaque exercice est formaté avec des commandes LaTeX personnalisées que j’ai définies dans l’en-tête :

\exercice{12345, name, date, 3, Étude de fonction}
\enonce{12345}{}
Soit f la fonction définie sur R par f(x) = x² + 2x - 3.
1. Montrer que f est continue sur R.
2. Déterminer les limites de f en ±∞.
\finenonce{12345}
\finexercice

Traitement par lots et génération de PDF

Pour gérer plusieurs chapitres, j’ai construit un système de traitement par lots simple. Le script lit les URLs depuis un fichier texte et les traite toutes :

if __name__ == "__main__":
    with open('pages.txt', 'r') as f:
        for line in f:
            page = line.strip()
            get_page(page)

L’exécution du scraper ressemble à ceci :

$ python grab.py
Fichier LaTeX généré : dump/Nombres complexes.tex
Fichier LaTeX généré : dump/Polynômes.tex  
Fichier LaTeX généré : dump/Fractions rationnelles.tex
Fichier LaTeX généré : dump/Espaces vectoriels.tex
...

Ensuite, un script bash compile tout en PDF :

#!/bin/bash
for tex_file in "dump"/*.tex; do
    filename=$(basename "$tex_file")
    echo "Processing $filename..."
    pdflatex -output-directory="output" "$tex_file"
    
    if [ $? -eq 0 ]; then
        echo "Successfully generated PDF for $filename"
    else
        echo "Error generating PDF for $filename"
    fi
done

Sortie console pendant la compilation :

$ ./gen.sh
Traitement de Nombres complexes.tex...
This is pdfTeX, Version 3...
...
Output written on output/Nombres complexes.pdf (47 pages, 892341 bytes).
PDF généré avec succès pour Nombres complexes.tex

Traitement de Polynômes.tex...
...
PDF généré avec succès pour Polynômes.tex

Ce que j’ai obtenu

Le système final me donne des feuilles d’exercices PDF propres organisées par chapitre et sujet. Chaque PDF contient :

  • Section exercices : Tous les problèmes clairement formatés avec des étoiles de difficulté
  • Section indices : Séparée pour que je puisse d’abord essayer les problèmes
  • Section solutions : Solutions complètes détaillées

Les PDF ont l’air professionnels et sont beaucoup plus faciles à imprimer et étudier que le site web. Je peux maintenant récupérer n’importe quel chapitre de Bibmath et avoir une version LaTeX en quelques secondes.

Démo 1 - PDF LaTeX généré
PDF LaTeX généré

Démo 2 - PDF LaTeX généré
PDF LaTeX généré

Voici la structure de fichiers typique après avoir tout exécuté :

dump/
  Nombres complexes.tex
  Polynômes.tex
  Espaces vectoriels.tex
  ...
output/
  Nombres complexes.pdf
  Polynômes.pdf
  Espaces vectoriels.pdf
  ...

Si vous voulez l’essayer

Le code est du Python simple avec BeautifulSoup et requests. Vous aurez besoin d’un fichier pages.txt avec les URLs Bibmath (une par ligne), plus des fichiers LaTeX d’en-tête/pied de page pour le style.

C’est spécifiquement construit pour la structure HTML de Bibmath, donc ça ne fonctionnera pas sur d’autres sites sans modification. Mais si vous étudiez les maths françaises et voulez des exercices mieux formatés, ça pourrait vous faire gagner du temps.