LLM-Based Parsing
Définition
Le LLM-Based Parsing utilise des modèles de langage pour extraire des données structurées depuis du HTML brut ou du texte. Au lieu de sélecteurs CSS fragiles, le LLM comprend sémantiquement le contenu et extrait les champs demandés. Cette approche est plus résiliente aux changements de structure des sites.
Deep Dive
LLM Parsing : quand l'IA remplace les sélecteurs CSS
Comparatif des approches
| Aspect | Sélecteurs CSS/XPath | LLM Parsing |
|--------|---------------------|-------------|
| Précision | Haute (si bien ciblés) | Variable (95%+) |
| Résilience | Fragile aux changements | Très résiliente |
| Coût | Minimal | Tokens LLM |
| Vitesse | Très rapide | Plus lent |
| Setup | Configuration par site | Prompt universel |
Cas d'usage idéaux
1. **Sites changeants** : E-commerce avec A/B testing fréquent
2. **Contenu non-structuré** : Articles, reviews, forums
3. **Extraction sémantique** : Sentiment, entités, relations
4. **Prototypage rapide** : POC sans développement
Architecture typique
```
HTML brut → Simplification (readability) → Prompt LLM → Parsing JSON → Validation
```
Optimisations de coût
Code Playground
Extracteur LLM avec simplification HTML, validation Pydantic et fallback sur parsing traditionnel
# Extraction de données par LLM avec validation Pydantic
from pydantic import BaseModel, Field
from typing import List, Optional
import json
# from openai import OpenAI
class ExtractedProduct(BaseModel):
"""Schéma de validation pour les produits extraits"""
name: str = Field(description="Nom du produit")
price: float = Field(description="Prix en euros")
description: Optional[str] = Field(description="Description courte")
availability: bool = Field(description="Disponibilité en stock")
rating: Optional[float] = Field(ge=0, le=5, description="Note sur 5")
class LLMExtractor:
"""Extracteur de données basé sur LLM avec fallback"""
def __init__(self, model: str = "gpt-4o-mini"):
self.model = model
# self.client = OpenAI()
def simplify_html(self, html: str) -> str:
"""Simplifie le HTML pour réduire les tokens"""
from selectolax.parser import HTMLParser
tree = HTMLParser(html)
# Supprimer les éléments non-pertinents
for tag in ['script', 'style', 'nav', 'footer', 'iframe', 'noscript']:
for node in tree.css(tag):
node.decompose()
# Supprimer les attributs sauf class et id
for node in tree.css('*'):
for attr in list(node.attributes.keys()):
if attr not in ['class', 'id', 'href', 'src', 'data-price']:
node.attrs.pop(attr, None)
return tree.html
def extract_product(self, html: str) -> ExtractedProduct:
"""Extrait un produit depuis du HTML via LLM"""
simplified = self.simplify_html(html)
prompt = f'''Extrait les informations du produit depuis ce HTML.
Retourne UNIQUEMENT un objet JSON valide avec ces champs:
- name (string): nom du produit
- price (number): prix en euros (nombre, pas de symbole)
- description (string ou null): description courte
- availability (boolean): true si en stock
- rating (number ou null): note sur 5
HTML:
{simplified[:8000]} # Limiter pour éviter overflow
JSON:'''
# En production:
# response = self.client.chat.completions.create(
# model=self.model,
# messages=[{"role": "user", "content": prompt}],
# temperature=0, # Déterminisme
# response_format={"type": "json_object"}
# )
# json_str = response.choices[0].message.content
# Placeholder
json_str = '{"name": "Product", "price": 99.99, "availability": true}'
# Validation avec Pydantic
data = json.loads(json_str)
return ExtractedProduct(**data)
def extract_with_fallback(self, html: str) -> ExtractedProduct:
"""Extraction avec fallback sur parsing traditionnel"""
try:
return self.extract_product(html)
except Exception as e:
print(f"LLM extraction failed: {e}")
return self._fallback_extraction(html)
def _fallback_extraction(self, html: str) -> ExtractedProduct:
"""Fallback vers extraction par sélecteurs"""
from selectolax.parser import HTMLParser
import re
tree = HTMLParser(html)
name = tree.css_first('h1')
price_elem = tree.css_first('[data-price], .price')
price = 0.0
if price_elem:
price_match = re.search(r'[d,]+.?d*', price_elem.text())
if price_match:
price = float(price_match.group().replace(',', ''))
return ExtractedProduct(
name=name.text(strip=True) if name else "Unknown",
price=price,
description=None,
availability=True,
rating=None
)
# Usage avec batch processing
async def batch_extract(urls: List[str], extractor: LLMExtractor) -> List[ExtractedProduct]:
"""Extraction batch avec rate limiting"""
import asyncio
import httpx
results = []
semaphore = asyncio.Semaphore(5) # Limite concurrence LLM
async with httpx.AsyncClient() as client:
async def process_one(url):
async with semaphore:
response = await client.get(url)
product = extractor.extract_with_fallback(response.text)
return product
tasks = [process_one(url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
return [r for r in results if isinstance(r, ExtractedProduct)]Avis d'Expert 2026
Le LLM-Based Parsing est une révolution pour le scraping en 2026, mais pas une solution miracle. Les coûts en tokens peuvent exploser à grande échelle. L'approche hybride (LLM pour cas difficiles, sélecteurs pour patterns stables) reste optimale. Les modèles vision (GPT-4V) ouvrent aussi la voie au scraping de screenshots.
Besoin d'aide sur LLM-Based Parsing ?
Nos experts peuvent vous accompagner sur vos projets de crawling et d'optimisation GEO.
Contactez l'expert Crawlers.fr