← Retour au blog

Proxies contextuels dans Playwright au lieu du navigateur : économie de mémoire et rotation IP efficace

Nous expliquons pourquoi l'attachement d'un proxy à BrowserContext dans Playwright est plus efficace que celui à un navigateur entier - avec du code, des benchmarks et des modèles de rotation prêts à l'emploi.

📅21 octobre 2026

Si vous exécutez des scripts de parsing ou de multi-comptes sur Playwright et que vous démarrez une instance de navigateur distincte pour chaque proxy, vous gaspillez de la mémoire et du CPU inutilement. Playwright permet d'assigner des proxies au niveau de BrowserContext, et non à l'ensemble du navigateur, ce qui change radicalement l'architecture des scripts : un processus de navigateur, des dizaines de contextes isolés avec différentes IP, cookies et empreintes digitales. Dans cet article, nous verrons comment cela fonctionne, du code en Python et Node.js, et pourquoi c'est particulièrement important lors de l'utilisation de proxies résidentiels et mobiles.

Problème : proxies sur browser.launch() — coûteux et peu pratiques

L'approche classique de nombreux scripts sur Playwright est la suivante : pour chaque proxy, chromium.launch({ proxy: {...} }) est appelé, créant un processus de navigateur distinct, qui fonctionne, puis se ferme, et le suivant est lancé avec un nouveau proxy. Si vous devez traiter 50 adresses IP en parallèle pour le parsing de Wildberries ou Ozon, cela signifie 50 processus Chromium complets en même temps — chacun consommant entre 150 et 300 Mo de RAM juste au démarrage, sans compter les onglets et le rendu.

En pratique, cela atteint un plafond déjà avec 15-20 navigateurs parallèles sur un VPS ordinaire avec 8 Go de mémoire. Le serveur commence à swapper, les scripts échouent avec des délais d'attente, certains proxies ne parviennent pas à fonctionner. Le problème ne vient pas des proxies ni de Playwright — le problème réside dans l'architecture : chaque lancement de navigateur entraîne tout le moteur de rendu, le processus GPU, le réseau et des dizaines de threads auxiliaires, alors qu'en réalité, vous avez juste besoin d'une nouvelle IP et d'une session propre.

Playwright résout ce problème de manière élégante : les proxies peuvent être définis non pas lors du lancement du navigateur, mais lors de la création d'un BrowserContext — un "bac à sable" léger et isolé à l'intérieur du même processus de navigateur. Un processus Chromium peut gérer des dizaines de contextes, chacun avec son propre proxy, ses cookies et son localStorage, sans interférence entre eux.

Navigateur vs BrowserContext : quelle est la différence

Browser — c'est le processus du moteur lui-même (Chromium, Firefox ou WebKit), qui est lancé une fois et vit longtemps. BrowserContext — c'est un environnement isolé à l'intérieur de ce processus, semblable à une fenêtre privée (incognito), avec ses propres cookies, cache, autorisations et, ce qui est important pour nous, ses propres paramètres réseau, y compris les proxies.

Si l'on compare avec des navigateurs anti-détection comme Dolphin Anty ou AdsPower — chaque "profil" est en réalité un conteneur isolé avec des cookies et une empreinte digitale. Playwright permet de construire une logique similaire par programmation : un navigateur — de nombreux contextes, chacun comme un "profil" distinct avec sa propre IP.

Paramètre Proxy sur Browser Proxy sur BrowserContext
Mémoire par IP 150-300 Mo (nouveau processus) 15-40 Mo (nouveau contexte)
Vitesse de changement d'IP 2-5 sec (redémarrage du navigateur) 0.1-0.3 sec (nouveau contexte)
Isolation des cookies Complète (processus différents) Complète (les contextes ne partagent pas les cookies)
Max. sessions parallèles sur 8 Go de RAM ~20-25 ~150-200

Proxies par contexte : code en Python

Voici un modèle de base : un navigateur est lancé une fois, puis un contexte distinct est créé pour chaque proxy. C'est l'API synchrone de Playwright, mais la logique est identique pour la version async.

from playwright.sync_api import sync_playwright

proxies = [
    {"server": "http://proxy1.example.com:8000", "username": "user1", "password": "pass1"},
    {"server": "http://proxy2.example.com:8000", "username": "user2", "password": "pass2"},
    {"server": "http://proxy3.example.com:8000", "username": "user3", "password": "pass3"},
]

with sync_playwright() as p:
    # Le navigateur est lancé UNE fois, sans proxy
    browser = p.chromium.launch(headless=True)

    for proxy in proxies:
        # Le proxy est assigné au contexte, et non au navigateur
        context = browser.new_context(
            proxy={
                "server": proxy["server"],
                "username": proxy["username"],
                "password": proxy["password"],
            },
            user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        )
        page = context.new_page()
        page.goto("https://httpbin.org/ip", timeout=30000)
        print(page.content())

        page.close()
        context.close()  # on ferme uniquement le contexte, le navigateur continue de vivre

    browser.close()

Le point clé est que browser.launch() est appelé une fois en dehors de la boucle, tandis que browser.new_context() est appelé à l'intérieur de la boucle pour les proxies. Pour les proxies avec authentification par nom d'utilisateur et mot de passe, Playwright ajoute automatiquement les en-têtes Basic Auth au niveau réseau, sans configuration supplémentaire.

Proxies par contexte : code en Node.js

La même logique en JavaScript avec async/await — souvent utilisée en conjonction avec des frameworks de parsing sur Node.js :

const { chromium } = require('playwright');

const proxies = [
  { server: 'http://proxy1.example.com:8000', username: 'user1', password: 'pass1' },
  { server: 'http://proxy2.example.com:8000', username: 'user2', password: 'pass2' },
];

(async () => {
  const browser = await chromium.launch({ headless: true });

  for (const proxy of proxies) {
    const context = await browser.newContext({
      proxy: {
        server: proxy.server,
        username: proxy.username,
        password: proxy.password,
      },
    });

    const page = await context.newPage();
    await page.goto('https://httpbin.org/ip', { timeout: 30000 });
    console.log(await page.content());

    await page.close();
    await context.close();
  }

  await browser.close();
})();

Pour le traitement parallèle de dizaines de proxies, les contextes peuvent être créés via Promise.all, sans attendre la fermeture du précédent — c'est le principal avantage de l'approche "un navigateur, plusieurs contextes" par rapport au lancement de processus distincts.

Rotation propre des proxies sans redémarrage du navigateur

La "rotation propre" signifie qu'à chaque changement d'IP, il ne reste aucune trace de la session précédente — ni cookies, ni cache, ni workers de service. Lors du redémarrage de l'ensemble du navigateur, cela est garanti automatiquement, mais coûteux en ressources. Lors de l'utilisation de contextes, la même chose est atteinte en fermant l'ancien contexte et en en créant un nouveau — l'opération prend des fractions de seconde.

async function rotateProxy(browser, proxyList, index) {
  const proxy = proxyList[index % proxyList.length];
  const context = await browser.newContext({ proxy });
  const page = await context.newPage();
  return { context, page };
}

// Utilisation : à chaque N-ième itération, changez d'IP
for (let i = 0; i < 100; i++) {
  if (i % 10 === 0) {
    const { context, page } = await rotateProxy(browser, proxies, i);
    // travaillez avec une nouvelle session pour 10 requêtes
  }
}

Ce modèle est particulièrement utile lors du parsing de marketplaces : par exemple, lors de la collecte des prix de Wildberries ou Ozon, où la protection contre les bots suit le nombre de requêtes d'une seule IP sur une période donnée. La rotation au niveau du contexte permet de changer d'IP toutes les 5-10 requêtes, sans perdre en performance à cause du redémarrage du navigateur.

Pour de tels scénarios, les proxies résidentiels sont bien adaptés — ils fournissent des IP d'utilisateurs réels, qui sont moins souvent marquées comme suspectes par les marketplaces, contrairement aux plages clairement serveur.

Comparaison de la mémoire et de la vitesse

Sur un VPS de test avec 8 Go de RAM et 4 vCPU, une vérification des deux approches a été réalisée sur la tâche de contournement de 100 pages avec différentes IP. Dans le premier cas, un chromium.launch() distinct était lancé pour chaque IP, dans le second — un seul navigateur avec la création de 100 contextes de manière séquentielle.

Métrique 100 lancements de browser.launch() 100 contextes dans 1 navigateur
Mémoire maximale ~6.8 Go ~1.9 Go
Temps d'exécution total 182 sec 97 sec
Nombre de sessions échouées par timeout 7 0

L'économie de mémoire dans ce test était d'environ 70 %, et le temps d'exécution total a été réduit de près de moitié grâce à l'absence de frais généraux liés au lancement et à la fermeture du processus Chromium à chaque fois. Sur de grands volumes — des milliers d'IP par jour — la différence devient encore plus marquée, et cela influence directement le nombre de proxies que vous pouvez utiliser sur un seul serveur sans mise à niveau du matériel.

Cas pratiques : parsing et multi-comptes

Parsing de marketplaces. Lors de la collecte des prix de Wildberries, Ozon ou Avito, un même navigateur maintient ouverts des dizaines de contextes, chacun avec une IP unique et son propre stockage de cookies. Cela réduit le risque de blocage par le modèle de requêtes et permet de collecter des données plus rapidement, sans attendre le redémarrage du processus entre les IP.

Multi-comptes et farming de comptes. Pour tester des campagnes publicitaires sur Facebook Ads ou TikTok Ads via des scripts Playwright, il est logique de garder chaque compte dans un contexte séparé avec son propre proxy et user-agent — cela imite le comportement des navigateurs anti-détection comme Multilogin ou GoLogin, mais sans licence pour l'anti-détection lui-même, entièrement par programmation.

Test géographique de la publicité. Les marketeurs qui doivent vérifier comment un créatif s'affiche dans différents pays peuvent utiliser des contextes avec différents proxies pour "visiter" les régions nécessaires au cours d'une seule session de navigateur, sans ouvrir manuellement des dizaines de fenêtres distinctes.

Pour des scénarios où il est important d'imiter le trafic mobile — par exemple, vérifier la version mobile d'un site ou la publicité sur TikTok Ads — il convient d'envisager des proxies mobiles, qui fournissent des IP de véritables opérateurs de téléphonie mobile et suscitent moins de soupçons auprès des systèmes anti-fraude.

Si la tâche consiste à effectuer un parsing rapide et stable sans nécessité d'une grande anonymité (par exemple, collecter des données publiques sur des sites sans protection anti-bots stricte), les proxies de centres de données offrent le meilleur rapport vitesse/coût sur de grands volumes de requêtes.

Erreurs fréquentes et comment les éviter

1. Les proxies sont définis dans launch(), et non dans new_context(). C'est l'erreur la plus courante des débutants — le proxy spécifié lors du lancement du navigateur s'applique à tous les contextes en même temps et ne peut pas être redéfini individuellement. Transmettez toujours le proxy dans new_context() si vous avez besoin de flexibilité sur l'IP.

2. Les anciens contextes ne sont pas fermés. Si vous créez des centaines de contextes et n'appelez pas context.close(), la mémoire du navigateur augmentera linéairement jusqu'à ce que le processus échoue par OOM. Fermez toujours le contexte après la fin de la tâche pour cette IP.

3. Un seul user-agent pour tous les contextes. Si le proxy change, mais que l'empreinte digitale du navigateur reste la même (user-agent, langue, fuseau horaire), les systèmes anti-bots des marketplaces et des réseaux sociaux peuvent tout de même lier les sessions entre elles. Changez le user-agent et le viewport avec le proxy pour chaque contexte.

4. Ignorer les délais d'attente des proxies. Les proxies résidentiels et mobiles répondent parfois plus lentement que les proxies de centres de données en raison de l'infrastructure réseau réelle. Définissez un délai raisonnable (20-40 secondes) lors de l'appel à page.goto(), sinon le script considérera le proxy fonctionnel comme "mort".

5. Trop de contextes dans un seul navigateur en même temps. Malgré l'économie de mémoire, la création incontrôlée de 500+ contextes actifs en même temps atteindra tout de même les limites du processus. Un équilibre raisonnable est de 30 à 80 contextes actifs simultanés par processus de navigateur, selon la quantité de RAM du serveur.

Conclusion

L'assignation de proxies au niveau de BrowserContext, et non à l'ensemble du navigateur — c'est une solution architecturale qui permet d'économiser jusqu'à 70 % de mémoire, d'accélérer la rotation des IP par dizaines et d'offrir une isolation propre des cookies entre les sessions sans redémarrages constants de Chromium. Pour le parsing de marketplaces, le test de publicités dans différentes régions et le multi-comptes, c'est un moyen direct d'augmenter le nombre de sessions parallèles sur le même serveur sans mise à niveau du matériel.

Si vous construisez un script sur Playwright pour collecter des données de Wildberries, Ozon ou pour travailler avec les cabinets publicitaires de Facebook Ads et TikTok Ads, la qualité même du pool de proxies est tout aussi importante que l'architecture du code. Pour les tâches où la fiabilité de l'IP et la résistance aux systèmes anti-bots sont critiques, il vaut la peine de commencer par des proxies résidentiels — elles se marient bien avec l'approche "un navigateur, plusieurs contextes" décrite ci-dessus.