Web Scraping Intelligent : Collecter des Données avec l IA
Votre équipe passe-t-elle des heures à copier-coller des données depuis des sites web concurrents, des plateformes d’annonces ou des bases de données publiques ? Selon une étude Statista, les entreprises qui s’appuient sur des données web structurées prennent des décisions 3 fois plus rapides que celles qui travaillent manuellement. Le web scraping — collecte automatisée de données web — existe depuis les années 2000, mais l’IA a radicalement changé la donne : là où il fallait des développeurs spécialisés et des semaines de travail pour extraire des données structurées depuis des sites hétérogènes, un pipeline IA peut aujourd’hui le faire en quelques heures.
Web scraping classique vs web scraping augmenté par l’IA
L’approche classique et ses limites
Le scraping traditionnel repose sur des sélecteurs CSS ou XPath : on cible un élément HTML précis et on en extrait le contenu.
# Approche classique avec BeautifulSoup
import requests
from bs4 import BeautifulSoup
def scrape_product_price(url: str) -> str:
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
# Sélecteur CSS fragile — cassé dès que le site change son HTML
price_element = soup.select_one('.product-price .amount')
return price_element.text if price_element else None Problèmes :
- Fragile : un changement de classe CSS dans le HTML casse le scraper
- Non générique : chaque site nécessite son propre code
- Incapable de comprendre le contexte (distinguer un prix principal d’un prix barré)
- Impuissant face aux sites JavaScript dynamiques
L’approche IA : robuste et générique
Avec un LLM, on décrit ce qu’on veut extraire en langage naturel. Le modèle comprend la structure sémantique de la page, pas seulement sa structure HTML.
import anthropic
import httpx
client = anthropic.Anthropic()
def scrape_with_llm(url: str, extraction_schema: dict) -> dict:
"""
Extrait des données structurées d'une page web via LLM.
extraction_schema : description des champs à extraire
"""
# Récupération du HTML brut
response = httpx.get(url, follow_redirects=True, timeout=15)
html_content = response.text
# Nettoyage : on ne garde que le texte visible (pas les styles/scripts)
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_content, 'html.parser')
# Suppression des éléments non pertinents
for tag in soup.find_all(['script', 'style', 'nav', 'footer', 'header']):
tag.decompose()
visible_text = soup.get_text(separator='\n', strip=True)[:8000] # Limite de contexte
schema_description = "\n".join([
f"- {key}: {desc}"
for key, desc in extraction_schema.items()
])
prompt = f"""Extrais les informations suivantes depuis ce contenu web.
Réponds uniquement avec un JSON valide contenant ces champs :
{schema_description}
Si une information est absente, mets null.
CONTENU DE LA PAGE :
{visible_text}"""
response = client.messages.create(
model="claude-haiku-4-5", # Haiku suffit pour cette tâche, moins coûteux
max_tokens=1024,
messages=[{"role": "user", "content": prompt}]
)
import json
return json.loads(response.content[0].text)
# Utilisation
schema = {
"nom_produit": "Nom complet du produit",
"prix_ttc": "Prix TTC en euros (nombre décimal)",
"prix_barre": "Ancien prix barré si disponible, sinon null",
"disponibilite": "En stock / Rupture / Précommande",
"description_courte": "Description principale du produit en 1-2 phrases"
}
data = scrape_with_llm("https://exemple-boutique.fr/produit/123", schema) Playwright pour les sites dynamiques
De nombreux sites modernes chargent leur contenu via JavaScript (React, Vue, Angular). BeautifulSoup ne voit que le HTML initial — il faut un vrai navigateur.
from playwright.async_api import async_playwright
import asyncio
async def scrape_dynamic_page(url: str) -> str:
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
args=['--no-sandbox', '--disable-setuid-sandbox']
)
context = await browser.new_context(
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
viewport={'width': 1920, 'height': 1080}
)
page = await context.new_page()
# Blocage des ressources inutiles (images, fonts) pour accélérer
await page.route("**/*.{png,jpg,jpeg,gif,webp,woff,woff2}",
lambda route: route.abort())
await page.goto(url, wait_until='networkidle', timeout=30000)
# Attendre que le contenu principal soit chargé
await page.wait_for_selector('[data-testid="product-title"], h1', timeout=10000)
# Scroll pour déclencher le lazy loading
await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
await asyncio.sleep(1)
content = await page.content()
await browser.close()
return content
# Pipeline complet
async def full_scrape_pipeline(url: str, schema: dict) -> dict:
html = await scrape_dynamic_page(url)
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'html.parser')
for tag in soup.find_all(['script', 'style']):
tag.decompose()
text = soup.get_text(separator='\n', strip=True)[:8000]
return extract_with_llm(text, schema) Stratégies anti-détection
Les sites mettent en place des protections contre le scraping abusif. Voici les bonnes pratiques pour rester sous le radar :
Délais et respect du robots.txt
import time
import random
from urllib.robotparser import RobotFileParser
def check_robots_allowed(url: str, user_agent: str = "*") -> bool:
from urllib.parse import urlparse
parsed = urlparse(url)
robots_url = f"{parsed.scheme}://{parsed.netloc}/robots.txt"
rp = RobotFileParser()
rp.set_url(robots_url)
try:
rp.read()
return rp.can_fetch(user_agent, url)
except Exception:
return True # En cas d'erreur, on suppose autorisé
class RespectfulScraper:
def __init__(self, min_delay: float = 1.0, max_delay: float = 3.0):
self.min_delay = min_delay
self.max_delay = max_delay
self.last_request_time = {}
def wait_politely(self, domain: str):
"""Respecte un délai entre requêtes vers le même domaine."""
last_time = self.last_request_time.get(domain, 0)
elapsed = time.time() - last_time
wait_time = random.uniform(self.min_delay, self.max_delay)
if elapsed < wait_time:
time.sleep(wait_time - elapsed)
self.last_request_time[domain] = time.time()
def scrape(self, url: str, schema: dict) -> dict | None:
from urllib.parse import urlparse
domain = urlparse(url).netloc
if not check_robots_allowed(url):
print(f"⚠️ Scraping interdit par robots.txt : {url}")
return None
self.wait_politely(domain)
return scrape_with_llm(url, schema) Rotation de proxies et d’User-Agents
Pour les scraping à grande échelle, la rotation de proxies est indispensable. Des services comme Bright Data, Oxylabs ou ScraperAPI proposent des proxies résidentiels qui passent la plupart des protections.
import httpx
import itertools
PROXIES = [
"http://user:pass@proxy1.example.com:8080",
"http://user:pass@proxy2.example.com:8080",
# ...
]
proxy_cycle = itertools.cycle(PROXIES)
def get_with_proxy(url: str) -> httpx.Response:
proxy = next(proxy_cycle)
with httpx.Client(proxy=proxy, timeout=15) as client:
return client.get(url) Pipeline de collecte avec enrichissement IA
L’IA ne sert pas seulement à l’extraction — elle peut aussi enrichir les données collectées :
from dataclasses import dataclass
from typing import Optional
import pandas as pd
@dataclass
class ProductData:
url: str
nom: str
prix: float
description: str
# Champs enrichis par IA
categorie_ia: Optional[str] = None
sentiment_description: Optional[str] = None
mots_cles_seo: Optional[list] = None
def enrich_product(product: ProductData) -> ProductData:
"""Enrichit un produit avec des analyses IA."""
enrichment_prompt = f"""Analyse ce produit et fournis un JSON avec :
- "categorie": catégorie principale parmi [Électronique, Vêtements, Maison, Alimentaire, Sport, Autre]
- "sentiment": sentiment de la description parmi [positif, neutre, négatif]
- "mots_cles": liste de 5 mots-clés SEO pertinents
Produit : {product.nom}
Description : {product.description}"""
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=300,
messages=[{"role": "user", "content": enrichment_prompt}]
)
import json
enrichment = json.loads(response.content[0].text)
product.categorie_ia = enrichment.get("categorie")
product.sentiment_description = enrichment.get("sentiment")
product.mots_cles_seo = enrichment.get("mots_cles", [])
return product
# Pipeline complet : collecte → extraction → enrichissement → export
async def run_scraping_campaign(urls: list[str], schema: dict) -> pd.DataFrame:
scraper = RespectfulScraper(min_delay=2.0, max_delay=5.0)
results = []
for url in urls:
raw_data = scraper.scrape(url, schema)
if raw_data:
product = ProductData(url=url, **raw_data)
product = enrich_product(product)
results.append(product)
df = pd.DataFrame([vars(r) for r in results])
df.to_csv("products_enriched.csv", index=False)
return df Considérations légales
Le web scraping est un sujet juridique complexe. Voici les points essentiels à retenir pour une PME française :
Ce qui est généralement autorisé :
- Scraper des données publiques sans authentification
- Respecter le robots.txt (même si non obligatoire, c’est la norme éthique)
- Scraper à une fréquence raisonnable sans impacter le serveur cible
Ce qui est risqué ou interdit :
- Contourner des systèmes d’authentification ou de protection (CFAA aux USA, directive NIS2 en Europe)
- Scraper des données personnelles sans base légale RGPD
- Reproduire des contenus protégés par le droit d’auteur
- Violer explicitement les CGU d’un service (peut engager la responsabilité civile)
Règle pratique : si les données sont publiquement accessibles, non personnelles, et que votre usage est interne (veille concurrentielle, agrégation de prix), vous êtes généralement dans une zone acceptable. En cas de doute, consultez un juriste.
Ce qu’il faut retenir
L’IA transforme le web scraping de deux manières complémentaires : elle rend l’extraction plus robuste (plus besoin de sélecteurs fragiles) et elle permet d’enrichir les données collectées à la volée (catégorisation, sentiment, mots-clés). Combinée avec Playwright pour les sites dynamiques et des pratiques de scraping respectueuses, cette approche permet de construire des pipelines de données web fiables et maintenables.
Le coût marginal d’analyse par page est faible (quelques centimes avec Haiku) et les gains de productivité sont considérables comparés à la collecte manuelle.
Vous avez un besoin de collecte de données web automatisée ? Notre équipe conçoit et déploie des pipelines de scraping sur mesure, adaptés à votre secteur et respectueux du cadre légal. Contactez-nous à contact@brio-novia.eu pour discuter de votre projet.