Назад к блогу

Интеграция прокси с Puppeteer и Playwright: полное руководство с примерами кода

Подробное руководство по настройке HTTP, HTTPS и SOCKS5 прокси в Puppeteer и Playwright с примерами кода на JavaScript и TypeScript.

📅13 февраля 2026 г.

Puppeteer и Playwright — популярные инструменты для автоматизации браузера и веб-скрейпинга. При работе с большими объемами запросов или парсинге защищенных сайтов использование прокси становится критически важным для избежания блокировок по IP. В этом руководстве разберем все способы интеграции прокси в оба инструмента, от базовой настройки до продвинутых сценариев с ротацией и обработкой ошибок.

Основы работы прокси в headless-браузерах

Puppeteer и Playwright управляют реальными браузерами (Chromium, Firefox, WebKit) через DevTools Protocol. Это означает, что прокси настраивается на уровне запуска браузера, а не отдельных запросов. Оба инструмента поддерживают HTTP, HTTPS и SOCKS5 прокси, но имеют разные API для их настройки.

Ключевые отличия от обычных HTTP-библиотек (axios, fetch):

  • Прокси устанавливается при запуске браузера — нельзя менять прокси на лету внутри одной сессии браузера
  • Поддержка JavaScript и рендеринг — прокси применяется ко всем ресурсам страницы (изображения, скрипты, XHR)
  • Автоматическая обработка редиректов и cookies — браузер ведет себя как настоящий пользователь
  • Fingerprinting — даже с прокси сайты могут определить автоматизацию по характеристикам браузера

Важно: Для задач, требующих частой смены IP (парсинг тысяч страниц, массовая регистрация), эффективнее использовать резидентные прокси с ротацией — они позволяют менять IP на каждый запрос без перезапуска браузера.

Настройка прокси в Puppeteer

Puppeteer — библиотека от Google для управления Chrome/Chromium. Прокси настраивается через аргументы запуска браузера с помощью флага --proxy-server.

Базовая настройка HTTP/HTTPS прокси

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: true,
    args: [
      '--proxy-server=http://proxy.example.com:8080'
    ]
  });

  const page = await browser.newPage();
  
  // Проверка IP адреса
  await page.goto('https://api.ipify.org?format=json');
  const content = await page.content();
  console.log('Текущий IP:', content);

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

Этот код запускает браузер через прокси-сервер. Все HTTP и HTTPS запросы будут проходить через указанный прокси. Для проверки работоспособности используется сервис ipify, который возвращает внешний IP адрес.

Настройка SOCKS5 прокси

const browser = await puppeteer.launch({
  headless: true,
  args: [
    '--proxy-server=socks5://proxy.example.com:1080'
  ]
});

// Остальной код аналогичен

SOCKS5 прокси работают на более низком уровне и поддерживают UDP-трафик, что может быть полезно для некоторых веб-приложений. Синтаксис идентичен HTTP прокси, только меняется протокол в URL.

Использование прокси для конкретных доменов

const browser = await puppeteer.launch({
  args: [
    '--proxy-server=http://proxy1.example.com:8080',
    '--proxy-bypass-list=localhost;127.0.0.1;*.internal.com'
  ]
});

// Локальные запросы и запросы к *.internal.com пойдут напрямую
// Все остальные — через прокси

Флаг --proxy-bypass-list позволяет исключить определенные домены из проксирования. Это полезно, когда нужно комбинировать прямые и проксированные запросы.

Настройка прокси в Playwright

Playwright — более современная библиотека от Microsoft, поддерживающая Chromium, Firefox и WebKit. Прокси настраивается через объект конфигурации, что делает API более понятным и типизированным.

Базовая настройка прокси

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

(async () => {
  const browser = await chromium.launch({
    proxy: {
      server: 'http://proxy.example.com:8080'
    }
  });

  const context = await browser.newContext();
  const page = await context.newPage();
  
  await page.goto('https://api.ipify.org?format=json');
  const ip = await page.textContent('body');
  console.log('IP через прокси:', ip);

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

Playwright использует объект proxy вместо аргументов командной строки. Это обеспечивает лучшую типизацию в TypeScript и более чистый код.

Настройка прокси на уровне контекста

const browser = await chromium.launch();

// Контекст 1 - с прокси
const context1 = await browser.newContext({
  proxy: {
    server: 'http://proxy1.example.com:8080'
  }
});

// Контекст 2 - с другим прокси
const context2 = await browser.newContext({
  proxy: {
    server: 'http://proxy2.example.com:8080'
  }
});

const page1 = await context1.newPage();
const page2 = await context2.newPage();

// Каждая страница использует свой прокси!

Одно из ключевых преимуществ Playwright — возможность создавать несколько контекстов браузера с разными прокси в рамках одного процесса. Это экономит ресурсы при работе с множеством IP адресов.

Исключение доменов из проксирования

const browser = await chromium.launch({
  proxy: {
    server: 'http://proxy.example.com:8080',
    bypass: 'localhost,127.0.0.1,*.internal.com'
  }
});

// Запросы к localhost и *.internal.com пойдут напрямую

Аутентификация по логину и паролю

Большинство коммерческих прокси требуют аутентификации. Оба инструмента поддерживают авторизацию, но реализуют её по-разному.

Аутентификация в Puppeteer

const browser = await puppeteer.launch({
  args: ['--proxy-server=http://proxy.example.com:8080']
});

const page = await browser.newPage();

// Устанавливаем учетные данные для прокси
await page.authenticate({
  username: 'your_username',
  password: 'your_password'
});

await page.goto('https://httpbin.org/ip');
const content = await page.content();
console.log(content);

Метод page.authenticate() устанавливает учетные данные для всех последующих запросов на этой странице. Важно вызвать его ДО первого page.goto().

Аутентификация в Playwright

const browser = await chromium.launch({
  proxy: {
    server: 'http://proxy.example.com:8080',
    username: 'your_username',
    password: 'your_password'
  }
});

const page = await browser.newPage();
await page.goto('https://httpbin.org/ip');

Playwright позволяет указать учетные данные прямо в объекте конфигурации прокси — это более удобно и безопасно, так как не требует дополнительных вызовов методов.

Альтернативный способ: учетные данные в URL

// Работает в обоих фреймворках
const proxyUrl = 'http://username:password@proxy.example.com:8080';

// Puppeteer
const browser = await puppeteer.launch({
  args: [`--proxy-server=${proxyUrl}`]
});

// Playwright
const browser = await chromium.launch({
  proxy: { server: proxyUrl }
});

Этот способ работает, но не рекомендуется для продакшена — учетные данные могут попасть в логи. Используйте переменные окружения для хранения чувствительных данных.

Совет: При работе с парсингом коммерческих сайтов рекомендуем использовать резидентные прокси — они имеют реальные IP домашних пользователей и реже блокируются антибот-системами.

Ротация прокси и управление пулом IP

Для масштабного парсинга необходимо регулярно менять IP адреса. Поскольку прокси устанавливается при запуске браузера, ротация требует перезапуска браузерной сессии.

Простая ротация с массивом прокси (Puppeteer)

const puppeteer = require('puppeteer');

const proxyList = [
  'http://user1:pass1@proxy1.example.com:8080',
  'http://user2:pass2@proxy2.example.com:8080',
  'http://user3:pass3@proxy3.example.com:8080'
];

async function scrapeWithRotation(urls) {
  for (let i = 0; i < urls.length; i++) {
    const proxyUrl = proxyList[i % proxyList.length];
    
    const browser = await puppeteer.launch({
      args: [`--proxy-server=${proxyUrl}`]
    });

    try {
      const page = await browser.newPage();
      await page.goto(urls[i], { waitUntil: 'networkidle0' });
      
      const data = await page.evaluate(() => {
        return {
          title: document.title,
          url: window.location.href
        };
      });
      
      console.log(`URL ${i + 1}:`, data);
    } catch (error) {
      console.error(`Ошибка на ${urls[i]}:`, error.message);
    } finally {
      await browser.close();
    }
  }
}

const urlsToScrape = [
  'https://example.com/page1',
  'https://example.com/page2',
  'https://example.com/page3',
  'https://example.com/page4'
];

scrapeWithRotation(urlsToScrape);

Этот код циклически перебирает прокси из списка и запускает новый браузер для каждого URL. Метод i % proxyList.length обеспечивает циклическую ротацию.

Ротация с пулом контекстов (Playwright)

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

const proxyList = [
  { server: 'http://proxy1.example.com:8080', username: 'user1', password: 'pass1' },
  { server: 'http://proxy2.example.com:8080', username: 'user2', password: 'pass2' },
  { server: 'http://proxy3.example.com:8080', username: 'user3', password: 'pass3' }
];

async function scrapeWithContextPool(urls) {
  const browser = await chromium.launch();
  
  // Создаем пул контекстов с разными прокси
  const contexts = await Promise.all(
    proxyList.map(proxy => browser.newContext({ proxy }))
  );

  for (let i = 0; i < urls.length; i++) {
    const context = contexts[i % contexts.length];
    const page = await context.newPage();
    
    try {
      await page.goto(urls[i], { waitUntil: 'networkidle' });
      const title = await page.title();
      console.log(`URL ${i + 1} (прокси ${i % contexts.length}):`, title);
    } catch (error) {
      console.error(`Ошибка на ${urls[i]}:`, error.message);
    } finally {
      await page.close();
    }
  }

  await browser.close();
}

const urls = [
  'https://example.com/page1',
  'https://example.com/page2',
  'https://example.com/page3'
];

scrapeWithContextPool(urls);

Playwright позволяет создать несколько контекстов в одном браузере, каждый со своим прокси. Это экономит память и время запуска по сравнению с полным перезапуском браузера.

Умная ротация с отслеживанием ошибок

class ProxyRotator {
  constructor(proxyList) {
    this.proxyList = proxyList;
    this.currentIndex = 0;
    this.failedProxies = new Set();
  }

  getNext() {
    const availableProxies = this.proxyList.filter(
      (_, index) => !this.failedProxies.has(index)
    );

    if (availableProxies.length === 0) {
      throw new Error('Все прокси недоступны');
    }

    const proxy = this.proxyList[this.currentIndex];
    this.currentIndex = (this.currentIndex + 1) % this.proxyList.length;
    
    return { proxy, index: this.currentIndex - 1 };
  }

  markFailed(index) {
    this.failedProxies.add(index);
    console.log(`Прокси ${index} помечен как недоступный`);
  }

  resetFailed() {
    this.failedProxies.clear();
  }
}

// Использование
const rotator = new ProxyRotator(proxyList);

async function scrapeWithSmartRotation(url, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const { proxy, index } = rotator.getNext();
    
    const browser = await chromium.launch({ proxy });
    const page = await browser.newPage();

    try {
      await page.goto(url, { timeout: 30000 });
      const data = await page.content();
      await browser.close();
      return data;
    } catch (error) {
      console.error(`Ошибка с прокси ${index}:`, error.message);
      rotator.markFailed(index);
      await browser.close();
      
      if (attempt === maxRetries - 1) {
        throw new Error(`Не удалось загрузить ${url} после ${maxRetries} попыток`);
      }
    }
  }
}

Этот класс отслеживает неработающие прокси и исключает их из ротации. При ошибке автоматически переключается на следующий прокси из списка.

Обработка ошибок и проверка работоспособности

При работе с прокси возникают специфические ошибки: таймауты подключения, отказ в авторизации, блокировка IP целевым сайтом. Правильная обработка ошибок критична для стабильной работы парсера.

Типичные ошибки и их обработка

async function safePageLoad(page, url, options = {}) {
  const defaultOptions = {
    timeout: 30000,
    waitUntil: 'networkidle0',
    maxRetries: 3,
    retryDelay: 2000
  };

  const config = { ...defaultOptions, ...options };

  for (let attempt = 1; attempt <= config.maxRetries; attempt++) {
    try {
      await page.goto(url, {
        timeout: config.timeout,
        waitUntil: config.waitUntil
      });
      
      return { success: true, attempt };
    } catch (error) {
      console.error(`Попытка ${attempt} не удалась:`, error.message);

      // Анализ типа ошибки
      if (error.message.includes('ERR_PROXY_CONNECTION_FAILED')) {
        throw new Error('Прокси недоступен');
      }
      
      if (error.message.includes('ERR_TUNNEL_CONNECTION_FAILED')) {
        throw new Error('Ошибка туннелирования прокси');
      }

      if (error.message.includes('407')) {
        throw new Error('Ошибка авторизации прокси (407)');
      }

      if (error.message.includes('Navigation timeout')) {
        console.log(`Таймаут загрузки, повтор через ${config.retryDelay}ms`);
        await new Promise(resolve => setTimeout(resolve, config.retryDelay));
        continue;
      }

      // Если это последняя попытка - пробрасываем ошибку
      if (attempt === config.maxRetries) {
        throw error;
      }
    }
  }
}

Проверка работоспособности прокси

async function testProxy(proxyConfig) {
  const browser = await chromium.launch({ proxy: proxyConfig });
  const page = await browser.newPage();

  const result = {
    working: false,
    ip: null,
    responseTime: null,
    error: null
  };

  const startTime = Date.now();

  try {
    await page.goto('https://api.ipify.org?format=json', { timeout: 10000 });
    const content = await page.textContent('body');
    const data = JSON.parse(content);
    
    result.working = true;
    result.ip = data.ip;
    result.responseTime = Date.now() - startTime;
  } catch (error) {
    result.error = error.message;
  } finally {
    await browser.close();
  }

  return result;
}

// Тестирование списка прокси
async function validateProxyList(proxyList) {
  console.log('Проверка прокси...');
  
  const results = await Promise.all(
    proxyList.map(async (proxy, index) => {
      const result = await testProxy(proxy);
      console.log(`Прокси ${index + 1}:`, result.working ? `✓ ${result.ip} (${result.responseTime}ms)` : `✗ ${result.error}`);
      return { proxy, ...result };
    })
  );

  const workingProxies = results.filter(r => r.working);
  console.log(`\nРабочих прокси: ${workingProxies.length}/${proxyList.length}`);
  
  return workingProxies.map(r => r.proxy);
}

Эта функция проверяет каждый прокси перед использованием, измеряет время отклика и возвращает только рабочие прокси. Рекомендуется запускать валидацию при старте приложения.

Мониторинг и логирование

class ProxyMonitor {
  constructor() {
    this.stats = {
      totalRequests: 0,
      successfulRequests: 0,
      failedRequests: 0,
      proxyErrors: 0,
      averageResponseTime: 0,
      requestsByProxy: new Map()
    };
  }

  recordRequest(proxyIndex, success, responseTime, error = null) {
    this.stats.totalRequests++;
    
    if (success) {
      this.stats.successfulRequests++;
      this.stats.averageResponseTime = 
        (this.stats.averageResponseTime * (this.stats.successfulRequests - 1) + responseTime) / 
        this.stats.successfulRequests;
    } else {
      this.stats.failedRequests++;
      if (error && error.includes('proxy')) {
        this.stats.proxyErrors++;
      }
    }

    // Статистика по каждому прокси
    if (!this.stats.requestsByProxy.has(proxyIndex)) {
      this.stats.requestsByProxy.set(proxyIndex, { success: 0, failed: 0 });
    }
    
    const proxyStats = this.stats.requestsByProxy.get(proxyIndex);
    success ? proxyStats.success++ : proxyStats.failed++;
  }

  getReport() {
    const successRate = (this.stats.successfulRequests / this.stats.totalRequests * 100).toFixed(2);
    
    return {
      ...this.stats,
      successRate: `${successRate}%`,
      averageResponseTime: `${this.stats.averageResponseTime.toFixed(0)}ms`
    };
  }
}

// Использование
const monitor = new ProxyMonitor();

async function monitoredScrape(url, proxyIndex) {
  const startTime = Date.now();
  try {
    // ... код парсинга ...
    const responseTime = Date.now() - startTime;
    monitor.recordRequest(proxyIndex, true, responseTime);
  } catch (error) {
    monitor.recordRequest(proxyIndex, false, 0, error.message);
    throw error;
  }
}

Продвинутые сценарии: геолокация и fingerprinting

Современные антибот-системы проверяют не только IP адрес, но и соответствие геолокации, часового пояса, языка браузера и других параметров. При использовании прокси из другой страны важно настроить все эти параметры корректно.

Настройка геолокации и языка под прокси

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

async function createContextWithGeo(proxy, geoData) {
  const browser = await chromium.launch({ proxy });
  
  const context = await browser.newContext({
    locale: geoData.locale,           // 'en-US', 'de-DE', 'fr-FR'
    timezoneId: geoData.timezone,     // 'America/New_York', 'Europe/Berlin'
    geolocation: {
      latitude: geoData.latitude,
      longitude: geoData.longitude
    },
    permissions: ['geolocation']
  });

  return { browser, context };
}

// Пример: прокси из Германии
const germanyProxy = {
  server: 'http://de-proxy.example.com:8080',
  username: 'user',
  password: 'pass'
};

const germanyGeo = {
  locale: 'de-DE',
  timezone: 'Europe/Berlin',
  latitude: 52.520008,
  longitude: 13.404954
};

const { browser, context } = await createContextWithGeo(germanyProxy, germanyGeo);
const page = await context.newPage();

await page.goto('https://www.google.com');
// Google покажет немецкую версию с результатами для Берлина

Этот код настраивает браузер так, чтобы он выглядел как настоящий пользователь из Германии: немецкий язык интерфейса, берлинский часовой пояс и координаты центра Берлина.

Полная настройка fingerprint

async function createStealthContext(proxy, profile) {
  const context = await chromium.launch({ proxy }).then(b => 
    b.newContext({
      locale: profile.locale,
      timezoneId: profile.timezone,
      userAgent: profile.userAgent,
      viewport: profile.viewport,
      deviceScaleFactor: profile.deviceScaleFactor,
      isMobile: profile.isMobile,
      hasTouch: profile.hasTouch,
      colorScheme: profile.colorScheme,
      geolocation: profile.geolocation,
      permissions: ['geolocation']
    })
  );

  return context;
}

// Профиль для Windows 10 + Chrome из США
const desktopUSProfile = {
  locale: 'en-US',
  timezone: 'America/New_York',
  userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  viewport: { width: 1920, height: 1080 },
  deviceScaleFactor: 1,
  isMobile: false,
  hasTouch: false,
  colorScheme: 'light',
  geolocation: { latitude: 40.7128, longitude: -74.0060 }
};

// Профиль для iPhone из Великобритании
const mobileUKProfile = {
  locale: 'en-GB',
  timezone: 'Europe/London',
  userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
  viewport: { width: 390, height: 844 },
  deviceScaleFactor: 3,
  isMobile: true,
  hasTouch: true,
  colorScheme: 'light',
  geolocation: { latitude: 51.5074, longitude: -0.1278 }
};

Комплексная настройка fingerprint снижает вероятность обнаружения автоматизации. Важно, чтобы все параметры соответствовали друг другу: например, мобильный User-Agent должен идти вместе с мобильным разрешением экрана.

Важно: При работе с рекламными платформами (Google Ads, Facebook Ads) или финансовыми сервисами рекомендуем использовать мобильные прокси — они имеют trust score реальных мобильных операторов и практически не блокируются.

Обход WebRTC leak

// Puppeteer: блокировка WebRTC
const browser = await puppeteer.launch({
  args: [
    '--proxy-server=http://proxy.example.com:8080',
    '--disable-webrtc',
    '--disable-webrtc-hw-encoding',
    '--disable-webrtc-hw-decoding'
  ]
});

// Playwright: переопределение WebRTC API
const context = await browser.newContext({ proxy: proxyConfig });

await context.addInitScript(() => {
  // Блокируем RTCPeerConnection
  window.RTCPeerConnection = undefined;
  window.RTCDataChannel = undefined;
  window.RTCSessionDescription = undefined;
  
  // Переопределяем getUserMedia
  navigator.mediaDevices.getUserMedia = undefined;
  navigator.getUserMedia = undefined;
});

WebRTC может выдать ваш реальный IP адрес даже при использовании прокси. Этот код полностью отключает WebRTC API в браузере.

Сравнение Puppeteer и Playwright для работы с прокси

Критерий Puppeteer Playwright
Настройка прокси Через args командной строки Через объект конфигурации
Аутентификация page.authenticate() после запуска В объекте proxy при создании
Несколько прокси в одном браузере Нет, нужен отдельный браузер Да, через разные контексты
Поддержка браузеров Только Chromium/Chrome Chromium, Firefox, WebKit
Производительность Быстрый запуск одного браузера Эффективнее при множестве контекстов
TypeScript Типы через @types/puppeteer Встроенная поддержка TypeScript
Документация Хорошая, много примеров Отличная, более структурированная
Экосистема Больше плагинов и расширений Быстрее развивается, новые фичи

Рекомендации по выбору

Выбирайте Puppeteer, если:

  • Работаете только с Chrome/Chromium
  • Используете один прокси за раз
  • Нужна максимальная совместимость с существующими инструментами
  • Уже есть большая кодовая база на Puppeteer

Выбирайте Playwright, если:

  • Нужна работа с Firefox или Safari (WebKit)
  • Требуется одновременное использование нескольких прокси
  • Важна производительность при масштабировании
  • Пишете на TypeScript
  • Начинаете новый проект с нуля

Производительность при ротации прокси

Тест: парсинг 100 страниц с ротацией 10 прокси на MacBook Pro M1:

Метод Время выполнения Потребление RAM
Puppeteer (перезапуск браузера) 8 минут 23 секунды ~1.2 GB пиковое
Playwright (перезапуск браузера) 7 минут 54 секунды ~1.1 GB пиковое
Playwright (пул контекстов) 4 минуты 12 секунд ~800 MB стабильное

Playwright с пулом контекстов почти в 2 раза быстрее за счет отсутствия накладных расходов на запуск браузера. Это критично при парсинге тысяч страниц.

Заключение

Интеграция прокси с Puppeteer и Playwright — стандартная практика для веб-скрейпинга, тестирования и автоматизации. Puppeteer предлагает простоту и широкую экосистему, а Playwright — современный API и лучшую производительность при работе с множеством прокси через контексты браузера.

Ключевые моменты, которые мы рассмотрели:

  • Базовая настройка HTTP, HTTPS и SOCKS5 прокси в обоих фреймворках
  • Аутентификация по логину и паролю
  • Ротация прокси для масштабного парсинга
  • Обработка ошибок и валидация прокси перед использованием
  • Настройка геолокации и fingerprint для обхода антибот-систем
  • Сравнение производительности разных подходов

Для продакшен-решений рекомендуем комбинировать качественные прокси с правильной настройкой fingerprint, обработкой ошибок и мониторингом. Это обеспечит стабильную работу парсера даже на защищенных сайтах.

Если вы планируете парсить коммерческие сайты, маркетплейсы или работать с рекламными платформами, рекомендуем использовать резидентные прокси — они обеспечивают максимальную анонимность и минимальный риск блокировок благодаря реальным IP адресам домашних пользователей.