Création d'un scraper intelligent avec le modèle BERT pour l'extraction précise de contenu et de titres

Création d'un scraper intelligent avec le modèle BERT pour l'extraction précise de contenu et de titres

Êtes-vous confronté à ce problème ? Vous développez un scraper qui collecte d'énormes quantités de données web, mais vous découvrez que le résultat est rempli de menus de navigation, de bannières publicitaires, de recommandations pertinentes et de commentaires d'utilisateurs, tandis que le contenu principal que vous recherchez est noyé dans le bruit. Le nettoyage manuel ? C'est long et fastidieux. L'utilisation d'expressions régulières avec un ensemble complexe de règles ? Ces règles deviennent inefficaces dès que le site web change, et leur maintenance devient un véritable cauchemar.

Les scrapers traditionnels peinent souvent à extraire le contenu essentiel des pages web. Ils dépendent soit de sélecteurs XPath ou CSS fixes, nécessitant une réécriture des règles dès qu'un site est mis à jour, soit de méthodes basées sur des heuristiques, mais face à la structure changeante des pages web, leur précision ressemble à un parcours de montagnes russes.

Aujourd'hui, explorons une approche plus intelligente : intégrer la technologie de "segmentation de texte" du traitement du langage naturel dans votre scraper. Plus précisément, nous utiliserons des modèles pré-entraînés comme BERT pour permettre à la machine d'apprendre par elle-même à identifier quelle partie d'une page web constitue le titre, quelle partie est le contenu principal, et quelle partie doit être considérée comme du bruit à rejeter. C'est comme donner à votre scraper des yeux capables de "comprendre" la structure d'une page web, sans avoir à lui apprendre manuellement les règles pour chaque site.

Dans la suite de cet article, nous examinerons comment fonctionne ce scraper intelligent combinant apprentissage profond et web scraping, depuis son concept fondamental jusqu'à la mise en pratique étape par étape, en passant par les résultats obtenus et les points d'attention à considérer. Vous découvrirez que l'amélioration de la "pureté" de la collecte de données n'est pas aussi complexe qu'on pourrait le penser.

1. Les limites des scrapers traditionnels et l'opportunité de l'intelligence

Décortiquons ensemble les obstacles qui se présentent entre le HTML brut d'une page web et le texte propre que nous souhaitons obtenir.

Lorsque vous utilisez des bibliothèques comme requests ou Scrapy pour récupérer une page web, vous obtenez une grande quantité de code HTML. En plus du contenu que nous recherchons, celui-ci contient de nombreuses "impuretés" :

  • Éléments de navigation : menus supérieurs, barres latérales, fil d'Ariane.
  • Composants interactifs : zones de commentaire, boutons de partage, sections de like.
  • Contenu commercial : diverses bannières publicitaires, liens promotionnels, contenus de marketing d'affiliation.
  • Informations non pertinentes : déclarations de droits d'auteur au pied de page, articles recommandés, boîtes d'information sur l'auteur.
  • Éléments décoratifs : icônes, lignes de séparation, espaces réservés.

Les solutions traditionnelles, comme l'utilisation de BeautifulSoup avec des sélecteurs méticuleusement écrits, consistent fondamentalement à jouer au "jeu de l'oie" avec la structure front-end du site. Le développeur doit analyser soigneusement l'arbre DOM du site cible pour identifier les caractéristiques uniques des contenus (comme des classes ou IDs spécifiques). Cette méthode fonctionne dans des cas limités, pour des sites peu nombreux et à structure fixe.

Mais les problèmes surviennent rapidement : les sites web évoluent. Les ingénieurs front-end modifient un nom de classe aujourd'hui, ajustent l'imbrication des div demain, et vos règles de scraping deviennent instantanément obsolètes. Sans parler du travail monumental requis pour maintenir un ensemble de règles personnalisé pour des centaines ou des milliers de sites web à structures différentes.

Cela nous amène à une nouvelle approche : pourquoi ne pas permettre à la machine de "lire" et de comprendre la structure sémantique d'une page web, afin d'identifier le contenu essentiel ? La technologie de segmentation de texte (Text Segmentation), notamment les modèles basés sur l'apprentissage profond, s'avère parfaitement adaptée à cette tâche. Son objectif est de découper une longue séquence de texte (dans notre cas, une séquence de nœuds textuels HTML concaténés) en segments ayant des sémantiques ou fonctions différentes, comme "titre", "contenu principal" ou "bruit". Des modèles comme BERT, excellents pour comprendre le contexte et la sémantique, sont manifestement plus fiables pour distinguer un titre d'article d'une publicité que le simple comptage de balises.

2. Le cœur de la solution : fonctionnement du modèle de segmentation BERT

Comment utiliser concrètement un modèle BERT pour "partager" le contenu d'une page web ? L'ensemble du processus peut être considéré comme une tâche d'étiquetage de séquence.

2.1 De la page web à la séquence de texte

Pour commencer, nous devons convertir le HTML non structuré en une entrée que le modèle peut traiter. Supprimer toutes les balises HTML pour ne conserver que le texte serait une approche trop brutale, car nous perdons toutes les informations structurelles. Une méthode plus efficace consiste à :

  1. Parser et parcourir : utiliser BeautifulSoup ou lxml pour analyser le HTML.
  2. Extraire les nœuds textuels : parcourir l'arbre DOM, extraire tous les nœuds feuilles ou des nœuds textuels à une profondeur spécifique. Simultanément, nous devons enregistrer certaines caractéristiques de chaque nœud textuel, qui serviront d'entrée auxiliaire pour le modèle :
    • Le contenu textuel lui-même.
    • La balise HTML à laquelle il appartient (comme p, h1, div, span).
    • Les noms de classes CSS ou l'ID du nœud.
    • La longueur du texte.
    • La profondeur dans l'arbre DOM.
  3. Construire la séquence : organiser ces nœuds textuels (avec leurs caractéristiques) dans l'ordre où ils apparaissent dans le DOM pour former une séquence.

2.2 La tâche du modèle : étiqueter chaque nœud

Maintenant, nous avons une séquence de nœuds textuels [nœud1, nœud2, ..., nœudN]. La tâche de notre modèle de segmentation BERT est de prédire une étiquette pour chaque nœud de la séquence.

Généralement, nous pouvons définir un ensemble d'étiquettes simple, par exemple :

  • TITRE : représente le titre principal de l'article.
  • CONTENU : représente le contenu principal de l'article.
  • AUTRE : représente tout le reste du bruit (navigation, publicité, commentaires, etc.).

Pendant l'entraînement, le modèle apprend à analyser la sémantique du texte, les relations contextuelles ainsi que les caractéristiques HTML que nous fournissons, pour déterminer "ce texte dans cette balise p ressemble-t-il à un paragraphe de presse ou à une partie d'un avertissement juridique ?"

2.3 Aperçu de l'architecture du modèle

Une architecture de modèle typique pour cette tâche pourrait ressembler à ceci :

  1. Fusion de caractéristiques : encoder le contenu textuel de chaque nœud en vecteurs sémantiques via BERT, puis encoder les caractéristiques HTML (balises, classes, etc.) via une couche d'intégration (Embedding) ou un petit réseau de neurones. Fusionner ensuite ces deux représentations (par exemple, en les concaténant).
  2. Modélisation de séquence : comme il existe des relations d'ordre et de structure entre les nœuds (par exemple, le contenu principal suit généralement le titre), nous devons capturer cette dépendance séquentielle. Nous pouvons ajouter une couche CRF (Conditional Random Field) ou LSTM/GRU bidirectionnel au-dessus de la sortie BERT. Le CRF est particulièrement adapté aux tâches d'étiquetage de séquence car il peut prendre en compte les probabilités de transition entre les étiquettes (par exemple, la probabilité qu'une étiquette TITRE soit suivie de CONTENU est élevée, tandis que la probabilité qu'elle soit suivie de AUTRE est faible).
  3. Classification de sortie : finalement, le modèle génère une distribution de probabilité pour chaque position dans la séquence, appartenant à l'ensemble {TITRE, CONTENU, AUTRE}. Nous retenons l'étiquette avec la probabilité la plus élevée comme résultat de la prédiction.

Entraîner un tel modèle nécessite de grandes quantités de données web étiquetées. Le travail d'étiquetage consiste à attribuer manuellement les étiquettes TITRE, CONTENU ou AUTRE à chaque nœud textuel d'un grand nombre de pages web. Bien que ce processus soit coûteux, une fois le modèle entraîné, il peut être généralisé à de nombreux sites web aux structures jamais vues auparavant.

3. Mise en pratique : du scraping au nettoyage du code

Après la théorie, passons à la pratique. Voici un flux de travail simplifié et des exemples de code montrant comment combiner un scraper avec un modèle de segmentation de texte. Nous utiliserons ici un modèle hypothétiquement déjà entraîné (dans une appplication réelle, vous devrez peut-être entraîner votre propre modèle ou trouver un modèle open-source).

3.1 Étape 1 : Récupération et analyse initiale

Commençons par utiliser la combinaison classique de requests et BeautifulSoup pour récupérer et analyser une page web.

import requests
from bs4 import BeautifulSoup
import re

def recuperer_et_parser(url):
    """Récupère une page web et la convertit en objet BeautifulSoup"""
    en_tetes = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
    try:
        reponse = requests.get(url, headers=en_tetes, timeout=10)
        reponse.raise_for_status()
        # On peut généralement spécifier un parseur tolérant, comme 'lxml'
        soupe = BeautifulSoup(reponse.content, 'lxml')
        return soupe
    except requests.RequestException as e:
        print(f"Échec de la récupération de {url}: {e}")
        return None

# Exemple : récupération d'un article d'actualité
url = "https://exemple.com/article-actualite"
soupe = recuperer_et_parser(url)
if soupe is None:
    exit()

3.2 Étape 2 : Construction de la séquence de nœuds textuels

C'est une étape cruciale où nous "aplatissons" l'arbre DOM en une séquence de nœuds, en collectant des caractéristiques pour chacun d'eux.

def extraire_noeuds_texte(soupe):
    """
    Extrait une séquence de nœuds textuels de l'objet BeautifulSoup.
    Retourne une liste où chaque élément est un dictionnaire contenant le contenu textuel et les caractéristiques.
    """
    noeuds = []
    # Nous parcourons tous les éléments, mais pouvons filtrer les balises script, style, etc.
    for element in soupe.find_all(text=True):
        parent = element.parent
        if parent.name in ['script', 'style', 'noscript']:
            continue

        texte = element.strip()
        if not texte or len(texte) < 5:  # Filtrer les textes courts ou vides
            continue

        info_noeud = {
            'texte': texte,
            'balise': parent.name,
            'classe': ' '.join(parent.get('class', [])),
            'id': parent.get('id', ''),
            # On peut ajouter plus de caractéristiques comme la longueur du texte, densité de liens, etc.
            'longueur_texte': len(texte),
            'densite_liens': len(parent.find_all('a')) / max(len(texte.split()), 1)
        }
        noeuds.append(info_noeud)
    return noeuds

noeuds_texte = extraire_noeuds_texte(soupe)
print(f"Total de {len(noeuds_texte)} nœuds textuels extraits.")
# Affichons les premiers nœuds pour vérification
for i, noeud in enumerate(noeuds_texte[:3]):
    print(f"Nœud{i}: [Balise:{noeud['balise']}] {noeud['texte'][:50]}...")

3.3 Étape 3 : Prédiction avec le modèle de segmentation

Supposons que nous ayons un modèle entraîné qui fournit une interface de prédiction simple. Ici, nous utilisons du pseudo-code pour illustrer le concept.

# Pseudo-code : chargement du modèle et du tokenizer (style de la bibliothèque Hugging Face Transformers)
# from transformers import AutoTokenizer, AutoModelForTokenClassification
# import torch
#
# nom_modele = "votre_modele_bert_seg_preentraîne"
# tokenizer = AutoTokenizer.from_pretrained(nom_modele)
# modele = AutoModelForTokenClassification.from_pretrained(nom_modele)
# modele.eval()

def predire_noeuds(noeuds, modele, tokenizer):
    """Simule le processus de prédiction du modèle"""
    # En pratique, vous devez convertir le texte et les caractéristiques des noeuds en tenseurs d'entrée
    # pour le modèle, y compris la tokenisation, la vectorisation des caractéristiques, le padding, etc.
    # Pour la démonstration, nous utilisons une règle heuristique simple (à remplacer par le modèle réel)
    predictions = []
    for noeud in noeuds:
        texte = noeud['texte']
        balise = noeud['balise']
        # Simuler une règle heuristique simple (remplacée par le modèle en réalité)
        if balise == 'h1' and 10 < len(texte) < 100:
            etiquette_predite = 'TITRE'
        elif balise == 'p' and len(texte) > 50 and noeud['densite_liens'] < 0.1:
            etiquette_predite = 'CONTENU'
        else:
            etiquette_predite = 'AUTRE'
        predictions.append(etiquette_predite)
    return predictions

# Simulation de la prédiction
etiquettes_predites = predire_noeuds(noeuds_texte, modele=None, tokenizer=None)  # Remplacer par le modèle réel

3.4 Étape 4 : Extraction du contenu et du titre

Une fois que nous avons les étiquettes prédites pour chaque nœud, nous pouvons facilement filtrer le contenu souhaité.

def extraire_contenu_et_titre(noeuds, etiquettes):
    """Extrait le titre et le contenu principal en fonction des étiquettes prédites"""
    titre = ''
    parties_contenu = []
    
    for noeud, etiquette in zip(noeuds, etiquettes):
        if etiquette == 'TITRE' and not titre:  # Généralement prendre le premier prédit comme titre principal
            titre = noeud['texte']
        elif etiquette == 'CONTENU':
            parties_contenu.append(noeud['texte'])
    
    # Fusionner les parties de contenu, avec des sauts de ligne pour conserver une certaine structure de paragraphes
    contenu_principal = '\n\n'.join(parties_contenu)
    return titre, contenu_principal

titre_article, contenu_article = extraire_contenu_et_titre(noeuds_texte, etiquettes_predites)

print("="*50)
print(f"Titre extrait :\n{titre_article}")
print("="*50)
print(f"Contenu extrait (500 premiers caractères) :\n{contenu_article[:500]}...")

4. Analyse des résultats et avantages

Une fois que vous avez mis en œuvre le flux de travail décrit ci-dessus, vous remarquerez plusieurs avantages par rapport aux méthodes traditionnelles.

En premier lieu, la précision est significativement améliorée. Pour les pages web modernes complexes et bruitées (comme celles remplies de pop-ups et de flux de recommandations), les extracteurs basés sur des règles sont susceptibles de "faire une erreur". Le modèle BERT, en comprenant la sémantique, peut mieux identifier les longs paragraphes qui "ressemblent" à du contenu principal, même s'ils sont enveloppés dans plusieurs couches de div. Par exemple, il peut distinguer le contenu principal d'un blog des commentaires d'utilisateurs tout aussi longs.

Ensuite, la capacité de généralisation est bien meilleure. Vous n'avez pas besoin d'analyser la structure DOM et d'écrire de nouveaux XPath pour chaque nouveau site. Un modèle bien entraîné sur une variété suffisante de données web peut gérer de nombreux modèles de sites qu'il n'a jamais vus. Cela réduit considérablement les coûts de développement et de maintenance de votre scraper. Votre scraper passe d'un agent nécessitant une configuration méticuleuse à un assistant "intelligent" capable d'apprendre de manière autonome.

De plus, les sorties sont plus propres et structurées. Vous n'obtenez pas un bloc de texte sans balises, mais un contenu déjà classifié en titre et paragraphes de contenu principal. Cela facilite grandement le traitement ultérieur des données (comme l'analyse de sentiment, l'extraction de mots-clés, le stockage en base de données).

Cependant, ce n'est pas une solution miracle. La prédiction du modèle nécessite des ressources de calcul, et sa vitesse peut être plus lente que l'analyse directe du DOM. Pour des scénarios de scraping massifs avec des exigences de vitesse élevée, cela pourrait être un facteur à considérer. Mais dans la plupart des scénarios où la qualité des données est plus importante que la vitesse de collecte (comme l'analyse d'opinions, l'agrégation de contenu, la construction de graphes de connaissances), cet échange est tout à fait justifié.

5. Conclusion et perspectives

En résumé, en intégrant le modèle de segmentation de texte BERT dans notre flux de travail de scraping, nous transformons fondamentalement le problème d'extraction de contenu web d'un problème d'"analyse syntaxique" dépendant de règles et de structure à un problème de "compréhension de lecture" basée sur la sémantique. L'avantage principal de cette approche réside dans son intelligence et sa capacité de généralisation.

Dans la pratique, pour les contenus principalement textuels comme les actualités, les blogs ou les publications de forums, l'amélioration de l'efficacité est la plus notable. Le modèle peut très efficacement identifier la partie principale d'un article. Cependant, pour certaines pages très atypiques, comme celles entièrement composées de cartes et de composants interactifs, ou avec un texte extrêmement rare, l'efficacité pourrait diminuer. Dans ces cas, il faudrait peut-être combiner certaines caractéristiques traditionnelles de la structure DOM, ou affiner spécifiquement le modèle pour certains types de pages.

Si vous souhaitez essayer cette approche, je vous suggère de commencer par expérimenter avec une ou deux sources de données importantes. Vous pouvez essayer d'utiliser certains modèles open-source pré-entraînés pour la segmentation de texte ou l'étiquetage de séquence comme point de départ, et les affiner avec un petit ensemble de données que vous avez collectées et étiquetées vous-même. Cela vous permettra de voir plus rapidement l'efficacité du modèle sur vos sites cibles. Le processus de traitement des données et d'ajustement du modèle vous donnera également une compréhension plus approfondie de la complexité des structures web.

Étiquettes: scraper web bert Traitement du Langage Naturel extraction de contenu apprentissage profond

Publié le 6 juin à 23h15