Use Cases (Mis à jour: 02/06/2026)

Web scraping sur avec Claude Code : Fetch, Playwright et journaux d'audit

Implémentez un web scraping sûr avec Claude Code : robots.txt, Fetch, Playwright, CSV et audit.

Web scraping sur avec Claude Code : Fetch, Playwright et journaux d'audit

Définir la limite avant de générer le code

Le web scraping consiste à lire des informations de pages web avec un programme. Il peut servir à surveiller votre propre site, inventorier de la documentation publique, vérifier des pages de prix ou contrôler la qualité d’un contenu. Mais une page publique ne signifie pas que tout peut être collecté sans condition. Claude Code accélère l’implémentation, donc la limite doit être écrite avant le code : données publiques seulement, respect des conditions d’utilisation et de robots.txt, requêtes espacées, pas de données personnelles sans base valable, et journal d’audit pour expliquer chaque ligne.

L’ordre le plus sûr pour débuter est de chercher d’abord une API officielle, un flux RSS, un sitemap, un export CSV ou un autre accès documenté. Ces sources sont plus stables que le HTML et leurs règles d’usage sont souvent plus claires. Ne passez au HTML que si le cas est légitime, de faible volume, et sans meilleure source structurée.

Cet article utilise Claude Code comme assistant d’implémentation, pas comme outil de contournement. Il ne couvre pas le bypass de login, CAPTCHA, protections anti-bot, collecte massive d’emails ou données restreintes. Si le flux touche des données personnelles, de la prospection commerciale ou des données réglementées, vérifiez d’abord la finalité, la base légale, la politique de confidentialité, la durée de conservation, l’opt-out et les obligations locales.

Gardez les références officielles ouvertes : RFC 9309 pour robots.txt, documentation Google robots.txt, MDN Fetch API et Playwright Browser contexts.

Flux recommandé

flowchart TD
  A["Un objectif précis"] --> B["Vérifier API, RSS et sitemap"]
  B --> C["Lire conditions et robots.txt"]
  C --> D{"HTML statique suffisant?"}
  D -->|Oui| E["Fetch page par page"]
  D -->|Non, autorisé| F["Playwright pour le DOM rendu"]
  E --> G["CSV avec URL et horodatage"]
  F --> G
  G --> H["Revue humaine avant usage"]

Ce flux rend la demande à Claude Code plus concrète. Ne demandez pas « scrape ce site ». Demandez plutôt : « récupère une seule page de cet origin autorisé, attends au moins deux secondes, arrête-toi si robots.txt bloque le chemin, et écris sourceUrl et fetchedAt dans le CSV ». Plus la frontière est précise, moins le code généré partira vers des boucles agressives, des sélecteurs fragiles ou des données à ne pas stocker.

Choisir Fetch ou Playwright

Utilisez fetch quand l’information nécessaire est déjà dans la réponse HTML. Documentation statique, articles, pages de prix publiques, pages de statut et contrôles issus d’un sitemap conviennent souvent. Fetch est facile à auditer : une requête HTTP, puis du texte. Il évite aussi le coût d’un navigateur complet.

Utilisez Playwright uniquement lorsqu’un vrai navigateur est nécessaire et que la page est à vous ou explicitement approuvée pour l’automatisation. Exemples : aperçu local, staging, QA interne, test de votre propre site. L’automatisation navigateur charge scripts, cookies, localStorage, permissions et comportement côté client. Il faut donc isoler les browser contexts pour éviter les mélanges de session.

Demandez à Claude Code de commencer par Fetch. Ajoutez Playwright seulement si le HTML statique ne suffit pas. En revue, cherchez les sleeps fixes, l’usage accidentel d’une session connectée, les sélecteurs basés sur des classes visuelles, l’absence de rate limit et les métadonnées source manquantes.

Cas d’usage

Premier cas : surveiller votre propre site. Vérifiez les pages de formation, produits, formulaires, articles, canonical URLs, titres et CTA. Comme le site est le vôtre, vous pouvez maintenir robots.txt, des sélecteurs stables et une fréquence raisonnable. Reliez cela aux opérations de contenu IA et à l’audit du funnel de contenu : le scraper détecte les changements, puis le workflow éditorial décide quoi corriger.

Deuxième cas : collecter des URLs de documentation publique. Une équipe peut indexer docs officielles, handbook interne ou base de connaissances publique. Souvent, le texte complet n’est pas nécessaire. URL, titre, horodatage et statut suffisent pour la recherche, la revue ou la planification éditoriale.

Troisième cas : vérifier des pages publiques de prix concurrents avec revue humaine. Un contrôle faible volume peut signaler un changement de nom de plan, de message promotionnel ou de structure publique. Mais la sortie ne doit pas devenir automatiquement une vérité commerciale. Les prix dépendent de la région, taxe, devise et conditions promotionnelles. Gardez source URL et date, puis faites relire par une personne.

Quatrième cas : recherche de leads avec garde-fous. Collecter en petite quantité un nom d’entreprise, un site officiel, un secteur ou une page de contact publique peut être raisonnable. Collecter aveuglément des emails personnels et les pousser dans une campagne outbound ne l’est pas. Si une prospection suit, ajoutez opt-out, identité d’expéditeur, listes de suppression et revue humaine. Combinez avec l’automatisation email Claude Code seulement si la collecte est minimale et autorisée.

Erreurs fréquentes

La première erreur est d’ignorer robots.txt et les conditions. robots.txt ne remplace pas l’analyse juridique, mais c’est une frontière technique publiée par le site. Les conditions peuvent aussi limiter automatisation, réutilisation ou surveillance commerciale.

La deuxième est de stocker tous les emails visibles. Une donnée personnelle visible reste potentiellement une donnée personnelle. Si vous n’en avez pas besoin, ne la collectez pas. Si vous en avez besoin, documentez finalité, base, conservation, accès, suppression et opt-out.

L’absence de rate limit est risquée. Des centaines de requêtes sans pause peuvent ressembler à une attaque. Utilisez petits lots, faible fréquence, User-Agent clair, retries limités et arrêt sur erreur.

Les sélecteurs fragiles créent des erreurs silencieuses. .card > div:nth-child(2) peut fonctionner aujourd’hui et casser demain. Préférez le HTML sémantique, time[datetime], main h1 ou des attributs data que vous contrôlez. Si un sélecteur requis manque, le job doit échouer et écrire un diagnostic.

Le contournement de protections n’est pas une fonctionnalité. Si Claude Code propose CAPTCHA bypass, scraping derrière login, identités tournantes ou contournement de limites, arrêtez et revenez à une source approuvée.

Ne stockez pas de données sensibles par défaut. HTML brut, données authentifiées, tokens, informations clients et données personnelles ne doivent pas finir dans un CSV non revu. Pour le cadre général, lisez les bonnes pratiques de sécurité Claude Code.

Exemple Fetch prêt à copier

Ce script fonctionne avec Node 18 ou plus. Il lit une seule page autorisée, vérifie robots.txt de manière conservatrice, espace les requêtes, extrait quelques champs et écrit un CSV avec sourceUrl et fetchedAt, plus un JSON d’audit.

// scrape-allowed-page.mjs
import { writeFile } from "node:fs/promises";

const USER_AGENT = "ClaudeCodeLabAuditBot/1.0 (+https://example.com/bot-info)";
const BOT_TOKEN = "ClaudeCodeLabAuditBot";
const targetUrl = new URL(process.env.SCRAPE_URL ?? "https://example.com/");
const allowedOrigins = (process.env.ALLOWED_ORIGINS ?? "https://example.com")
  .split(",")
  .map((value) => new URL(value.trim()).origin);
const delayMs = Number.parseInt(process.env.REQUEST_DELAY_MS ?? "2000", 10);

if (!allowedOrigins.includes(targetUrl.origin)) {
  throw new Error(`Blocked by allowlist: ${targetUrl.origin}`);
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function fetchText(url, accept) {
  await sleep(delayMs);
  return fetch(url, {
    headers: {
      "user-agent": USER_AGENT,
      accept,
    },
  });
}

async function loadRobots(origin) {
  const robotsUrl = new URL("/robots.txt", origin);
  const response = await fetchText(robotsUrl, "text/plain");
  if (response.status === 404) {
    return { url: robotsUrl.toString(), status: response.status, text: null };
  }
  if (!response.ok) {
    throw new Error(`robots.txt check failed: HTTP ${response.status}`);
  }
  return {
    url: robotsUrl.toString(),
    status: response.status,
    text: await response.text(),
  };
}

function parseRobots(text) {
  const groups = [];
  let agents = [];
  let rules = [];

  function commit() {
    if (agents.length > 0) {
      groups.push({ agents, rules });
    }
    agents = [];
    rules = [];
  }

  for (const rawLine of text.split(/\r?\n/)) {
    const cleaned = rawLine.split("#")[0].trim();
    if (!cleaned) continue;
    const separator = cleaned.indexOf(":");
    if (separator === -1) continue;

    const field = cleaned.slice(0, separator).trim().toLowerCase();
    const value = cleaned.slice(separator + 1).trim();

    if (field === "user-agent") {
      if (rules.length > 0) commit();
      agents.push(value.toLowerCase());
      continue;
    }

    if ((field === "allow" || field === "disallow") && agents.length > 0) {
      rules.push({ type: field, path: value });
    }
  }

  commit();
  return groups;
}

function escapeRegExp(value) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function pathMatches(pattern, path) {
  if (!pattern) return false;
  const exact = pattern.endsWith("$");
  const normalized = exact ? pattern.slice(0, -1) : pattern;
  const source = `^${escapeRegExp(normalized).replace(/\\\*/g, ".*")}${exact ? "$" : ""}`;
  return new RegExp(source).test(path);
}

function isAllowedByRobots(robotsText, url) {
  if (robotsText === null) {
    return process.env.ALLOW_WITHOUT_ROBOTS === "true";
  }

  const groups = parseRobots(robotsText);
  const bot = BOT_TOKEN.toLowerCase();
  const exactGroups = groups.filter((group) =>
    group.agents.some((agent) => agent !== "*" && bot.includes(agent)),
  );
  const fallbackGroups = groups.filter((group) => group.agents.includes("*"));
  const selectedGroups = exactGroups.length > 0 ? exactGroups : fallbackGroups;
  const rules = selectedGroups.flatMap((group) => group.rules);
  const targetPath = `${url.pathname}${url.search}`;
  let winner = null;

  for (const rule of rules) {
    if (!pathMatches(rule.path, targetPath)) continue;
    const length = rule.path.replace(/[*$]/g, "").length;
    if (!winner || length > winner.length || (length === winner.length && rule.type === "allow")) {
      winner = { type: rule.type, length };
    }
  }

  return winner ? winner.type === "allow" : true;
}

function normalizeText(value) {
  return value
    .replace(/<script[\s\S]*?<\/script>/gi, " ")
    .replace(/<style[\s\S]*?<\/style>/gi, " ")
    .replace(/<[^>]*>/g, " ")
    .replace(/&amp;/g, "&")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/\s+/g, " ")
    .trim();
}

function firstMatch(html, pattern) {
  const match = html.match(pattern);
  return match ? normalizeText(match[1]) : "";
}

function extractPageSummary(html) {
  const metaMatch =
    html.match(/<meta\s+[^>]*name=["']description["'][^>]*content=["']([^"']*)["'][^>]*>/i) ??
    html.match(/<meta\s+[^>]*content=["']([^"']*)["'][^>]*name=["']description["'][^>]*>/i);

  return {
    title: firstMatch(html, /<title[^>]*>([\s\S]*?)<\/title>/i),
    h1: firstMatch(html, /<h1[^>]*>([\s\S]*?)<\/h1>/i),
    metaDescription: metaMatch ? normalizeText(metaMatch[1]) : "",
    linkCount: [...html.matchAll(/<a\s+[^>]*href=["'][^"']+["']/gi)].length,
  };
}

function csvEscape(value) {
  const text = String(value ?? "");
  return /[",\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
}

const robots = await loadRobots(targetUrl.origin);
if (!isAllowedByRobots(robots.text, targetUrl)) {
  throw new Error(`Blocked by robots.txt: ${targetUrl.toString()}`);
}

const response = await fetchText(targetUrl, "text/html");
if (!response.ok) {
  throw new Error(`Page fetch failed: HTTP ${response.status}`);
}

const html = await response.text();
const fetchedAt = new Date().toISOString();
const row = {
  sourceUrl: targetUrl.toString(),
  fetchedAt,
  ...extractPageSummary(html),
};
const headers = ["sourceUrl", "fetchedAt", "title", "h1", "metaDescription", "linkCount"];
const csv = [headers.join(","), headers.map((header) => csvEscape(row[header])).join(",")].join("\n");

await writeFile("scrape-output.csv", `${csv}\n`, "utf8");
await writeFile(
  "scrape-audit.json",
  JSON.stringify(
    {
      checkedAt: fetchedAt,
      userAgent: USER_AGENT,
      robotsUrl: robots.url,
      robotsStatus: robots.status,
      allowedOrigins,
      sourceUrl: row.sourceUrl,
    },
    null,
    2,
  ),
  "utf8",
);

console.log(`Saved scrape-output.csv for ${row.sourceUrl}`);

Sous PowerShell : $env:SCRAPE_URL="https://your-domain.example/page"; $env:ALLOWED_ORIGINS="https://your-domain.example"; node scrape-allowed-page.mjs. La sortie reste volontairement simple : une ligne CSV et un fichier JSON d’audit.

Playwright seulement pour vos pages

Cet exemple Playwright vérifie des sélecteurs rendus sur votre site ou un aperçu local. Il ne sert pas à contourner les protections d’un site tiers.

// check-own-site-selectors.mjs
import { writeFile } from "node:fs/promises";
import { chromium } from "playwright";

const target = process.env.LOCAL_PREVIEW_URL ?? "http://127.0.0.1:4321/blog/claude-code-web-scraping/";
const allowedPrefixes = [
  "http://127.0.0.1:",
  "http://localhost:",
  "https://claudecodelab.com/",
];

if (!allowedPrefixes.some((prefix) => target.startsWith(prefix))) {
  throw new Error(`Playwright check is limited to owned or local pages: ${target}`);
}

const browser = await chromium.launch();
const context = await browser.newContext({
  userAgent: "ClaudeCodeLabAuditBot/1.0 local-preview-check",
});
const page = await context.newPage();

await page.goto(target, { waitUntil: "domcontentloaded" });

const checks = [
  { name: "article title", selector: "main h1, article h1" },
  { name: "updated date", selector: "time, [data-updated-date]" },
  { name: "main article", selector: "main article, article" },
];
const results = [];

for (const check of checks) {
  const locator = page.locator(check.selector);
  const count = await locator.count();
  const firstText = count > 0 ? ((await locator.first().textContent()) ?? "").trim().slice(0, 120) : "";
  results.push({ ...check, count, firstText });
}

await writeFile(
  "selector-audit.json",
  JSON.stringify({ target, checkedAt: new Date().toISOString(), results }, null, 2),
  "utf8",
);

await context.close();
await browser.close();

const missing = results.filter((result) => result.count === 0);
if (missing.length > 0) {
  throw new Error(`Missing selectors: ${missing.map((result) => result.name).join(", ")}`);
}

console.log(`Saved selector-audit.json for ${target}`);

Les browser contexts isolent cookies, localStorage et permissions. N’utilisez pas une vraie session connectée sans approbation explicite et base de traitement claire.

Prompt pour Claude Code

Un prompt plus sûr :

Ajoute un scraper d’une seule page pour un origin en allowlist. Documente d’abord l’existence d’une API officielle, RSS ou sitemap. Vérifie robots.txt avant de récupérer le HTML. Le CSV doit contenir sourceUrl et fetchedAt. Ne collecte pas emails, noms personnels, données authentifiées ou secrets. Ne contourne pas CAPTCHA, login wall, protection bot ou rate limit. Ajoute throttling, arrêt sur blocage, et montre node --check pour les fichiers JavaScript.

Le but est que Claude Code implémente un flux auditable, pas qu’il décide de la limite légale. Avant une exécution planifiée, relisez diff, URLs cibles, champs sauvegardés, cadence, suppression et exemples de sortie.

Checklist opérationnelle

  • Chercher API, RSS, sitemap ou export avant HTML.
  • Confirmer que la page est publique et l’usage conforme aux conditions.
  • Respecter robots.txt et journaliser la vérification.
  • Utiliser une allowlist d’origin et de petits lots.
  • Ajouter délai, retries limités et arrêt sur erreur.
  • Utiliser un User-Agent clair.
  • Sauvegarder URL source, horodatage, méthode et robots status.
  • Préférer des sélecteurs sémantiques ou attributs data contrôlés.
  • Ne pas stocker par défaut données personnelles, secrets, sessions ou HTML brut.
  • Relire des échantillons avant décision métier.

Si le CSV est ouvert dans un tableur, pensez au CSV injection. Toute chaîne venant du web est une entrée non fiable. Reliez ce flux à la sécurité, l’automatisation de contenu et l’outreach au lieu de l’envoyer silencieusement vers un CRM.

Formation et conseil

Le code est la partie facile. Le vrai sujet est de décider ce qu’il ne faut pas collecter, comment prouver que la collecte était autorisée, comment relire les changements et comment supprimer les sorties périmées. ClaudeCodeLab peut transformer cela en règles CLAUDE.md, contrôles Playwright, journaux CSV et approbations humaines via la formation et consultation Claude Code.

Pour démarrer seul, limitez-vous à une page, une exécution, des données publiques. N’augmentez pas le volume avant d’avoir audit, arrêt sur échec et revue humaine.

Résumé

Un web scraping sûr avec Claude Code commence par les limites, pas les sélecteurs. Priorisez API et sitemaps, utilisez Fetch pour les pages statiques autorisées, et réservez Playwright aux pages dynamiques que vous possédez ou qui sont approuvées. Journalisez toujours URL, date, statut robots et User-Agent.

Évitez aussi les violations de conditions, la collecte aveugle d’emails, le contournement de limites, les échecs silencieux de sélecteurs, le bypass de protections et les dumps de données sensibles. Claude Code accélère la mécanique, mais le flux n’est prêt que si une personne peut expliquer finalité, source, date et suppression.

En testant cette approche, Masa a constaté qu’une première version limitée à une page, une ligne CSV et un JSON d’audit rendait la revue beaucoup plus simple. sourceUrl et fetchedAt ont été particulièrement utiles pour expliquer les vérifications de prix et de pages internes. Les prototypes partis trop vite sur beaucoup de pages ont dû être réécrits, car les erreurs de sélecteurs et les oublis de politique étaient difficiles à tracer.

#Claude Code #web scraping #Fetch API #Playwright #robots.txt
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.