Playwright не может получить файлы cookie, недоступные для JS, библиотекa Camoufox

Файлы cookie для аутентификации присутствуют в браузере (как показано в dev tools), но Playwright не фиксирует/копирует их должным образом. Это обычная проблема с тем, как Playwarch обрабатывает файлы cookie?

Проблема

В dev tools можно увидеть файлы cookie для аутентификации

Но в сохраненных файлах cookie мы получаем только: GAESA, _dd_s (файлы cookie без авторизации) Файлы cookie Cloudflare (cf_clearance, __cf_bm)

Есть какой-нибудь выход?

Основная / фоновая проблема заключается в том, что Playstation не подключается к уже открытому/запущенному браузеру под определенным портом, а скорее открывает новый браузер, таким образом, пропуская существующий контекст, файлы cookie:

for i, (port, path, name) in enumerate(zip(config["ports"], config["paths"],     config["names"])):
# Create server thread
server_thread = threading.Thread(target=run_server, args=(port, path))
servers.append(server_thread)

# Create browser automation thread
browser_url = f"ws://localhost:{port}/{path}"
browser_thread = threading.Thread(
    target=automate_browser, 
    args=(browser_url, name)
)
browser_automations.append(browser_thread)

Обновление

  1. Python/Playwright открывает Camoufox браузер (похоже внутри некого сервера) на определённом порту — ✅

     for i, (port, path, name) in enumerate(zip(config["ports"], config["paths"], config["names"])):
     # Create server thread
     server_thread = threading.Thread(target=run_server, args=(port, path))
     servers.append(server_thread)
    
     # Create browser automation thread
     browser_url = f"ws://localhost:{port}/{path}"
     browser_thread = threading.Thread(
         target=automate_browser, 
         args=(browser_url, name)
     )
     browser_automations.append(browser_thread)
    
     # Start all servers with delays
     print(f"Starting {len(servers)} servers...")
     for i, server in enumerate(servers):
         print(f"Starting server {i+1} on port {config['ports'][i]}...")
         server.start()
         time.sleep(config["server_settings"]["start_delay"])
    
     print("All servers started. Starting browser automation...")
    
     # Start all browser automations with delays
     for i, browser_automation in enumerate(browser_automations):
         browser_automation.start()
         if i < len(browser_automations) - 1:  # Don't sleep after the last one
         time.sleep(config["server_settings"]["browser_delay"])
    

Выходит что browser_url будет такой: ws://localhost:2525/browser2525, где 2525 - некий порт для последующего подключения.

  1. Вручную логинюсь в google и Midjourney (на основе google логина) — ✅
    Этот Camoufox браузер должен оставаться работающим долгое время чтобы другие скрипты могли к нему подключаться и работать с ним.

  2. Другим скриптом пытаюсь подключиться к открытому браузеру — ❌

     async def connect_to_session(self, port): 
     if not await self.check_camoufox_availability(port):
         raise Exception(f"Camoufox WebSocket port {port} not accessible")
    
     ws_url = f"ws://localhost:{port}/browser{port}"
    
     browser = await self.playwright.firefox.connect(ws_url)
    

ws_url (Web Socket URL) идентична browser_url в коде пункта 1

Но при выполнении кода Playwright не подключается к существующему открытому браузеру а запускает новый, соответственно БЕЗ контекта, пустой... ❌

Обновление 2

Camoufox: запускает как сервер так и браузер.

Разница заключается в принципе работы Camoufox по сравнению с обычным Firefox.

Ключевое отличие

Обычный Firefox:

  • Прямой запуск: playwright.firefox.launch() → Окно браузера появляется сразу.
  • Сервер не требуется: Playwright напрямую управляет процессом браузера.
  • Простота: один процесс, одно соединение.

Camoufox:

  • Серверная архитектура: launch_server() → Создает сервер WebSocket → Браузер подключается к нему.
  • Два процесса:
    1. Серверный процесс (прослушивает порт XXXXX)
    2. Процесс браузера (подключается к серверу)
  • WebSocket-связь: все команды проходят через сервер

? Почему Camoufox нуждается в сервере:

  • Функции защиты от обнаружения: сервер обрабатывает ротацию прокси, маскировку отпечатков пальцев и т. д.
  • Удаленное управление: несколько клиентов могут подключаться к одному и тому же экземпляру браузера
  • Скрытый режим: сервер управляет возможностями браузера по скрытию
  • Управление прокси: сервер обрабатывает сложные конфигурации прокси

? Поток:

     launch_server() → Сервер WebSocket (порт XXXXX) → Процесс браузера
                            ↑
                  Playwright подключается сюда

? Вот почему:

  • launch_server() блокирует — он запускает сервер на неопределенный срок.
  • Нам нужны потоки — чтобы сервер не блокировал основной поток.
  • Появляется окно браузера — когда процесс браузера подключается к серверу.
  • Браузер быстро закрывается — если процесс сервера завершается или не запускается.

Camoufox is a stealthy, customized Firefox browser designed specifically for web scraping and bot detection evasion.


Ответы (1 шт):

Автор решения: Amgarak

Для своих задач я сильно не мудрил, просто использую пользовательские профили - user_data_dir.

Первый скрипт у меня ничего особого не делает, просто запускает браузер с указанием директории профиля:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    context = p.chromium.launch_persistent_context(
        user_data_dir="./my-user-data",
        headless=False,
        args=[
            "--disable-blink-features=AutomationControlled",
            "--start-maximized",
        ],
    )

    page = context.new_page()
    print("Открываем Google... авторизуйся и можешь завершать скрипт.")
    page.goto("https://www.google.com/")

    input("Проверь поведение и нажми Enter...")
    context.close()

Я вручную захожу на нужные сайты (Google, Yandex и т.д.), прохожу авторизацию - все куки, токены, сессии и т.д. автоматически сохраняются в папку ./my-user-data. Так можно наделать сколько угодно профилей ./my-user-data1..n и потом ловко жонглировать сессиями уже в боевом скрипте.


Второй (боевой) скрипт запускает новый экземпляр браузера с нужным user_data_dir и соответственно содержит всю логику управления браузером:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    context = p.chromium.launch_persistent_context(
        user_data_dir="./my-user-data",
        headless=False,
        args=["--disable-blink-features=AutomationControlled", "--start-maximized"],
    )
    page = context.new_page()
    print("Открываем Google...")
    page.goto(f"https://www.google.com/")
    
    # Твоя логика управления браузером.

    input("Проверь поведение и нажми Enter...")
    context.close()

И соответственно все авторизации остаются. Не надо бороться с капчами и т.д. Единственный минус что сессии на некоторых ресурсах протухают и нужно заново авторизовываться. Но для этого я просто запускаю первый скрипт, подставляю нужную папку в user_data_dir= и так же вручную перелогиниваюсь.


P.S. Но при новом открытии браузера будет задержка нежели при подключении к уже открытому...@Igor Savinkin

Я использую пул потоков для запуска множества браузеров, и мне нормально?.
Да, есть небольшая задержка, но зато я гарантированно получаю полную сессию и при этом сильно не ломаю себе мозг в плане реализации. Для масштабируемости и надёжности - это оптимальный компромисс в моём случае.


Но здесь Вы запускаете chromium. А мы хотели бы сделать подобное с Camoufox. @Igor Savinkin - а в чём проблема? Принцип +- тот же ?‍♀️

from camoufox.sync_api import Camoufox

with Camoufox(
    persistent_context=True, user_data_dir="./my-user-data-camoufox", headless=False
) as context:

    page = context.new_page()
    print("Открываем Google... авторизуйся и можешь завершать скрипт.")

    page.goto("https://www.google.com/")

    input("Проверь поведение и нажми Enter...")
→ Ссылка