fix: авто-перезапуск браузера при закрытом контексте (BrowserContext.new_page: has been closed)
This commit is contained in:
1 parent
4dcaffd4c4
commit
e4cd06a23d
1 file changed
+51
-11
+51
-11
@@ -42,6 +42,7 @@ SUBMIT_BUTTONS = [
|
|||||||
# Доступны ТОЛЬКО из браузерного потока.
|
# Доступны ТОЛЬКО из браузерного потока.
|
||||||
_browsers = {}
|
_browsers = {}
|
||||||
_pages = {}
|
_pages = {}
|
||||||
|
_playwrights = {}
|
||||||
|
|
||||||
# ---------- Браузерный поток ----------
|
# ---------- Браузерный поток ----------
|
||||||
|
|
||||||
@@ -111,9 +112,8 @@ def get_proxy_config():
|
|||||||
|
|
||||||
# ---------- Внутренние функции (только из браузерного потока) ----------
|
# ---------- Внутренние функции (только из браузерного потока) ----------
|
||||||
|
|
||||||
def _launch_context(account_dir):
|
def _launch_context_with(p, account_dir):
|
||||||
"""Запускает браузер с профилем аккаунта и подставляет cookies сессии."""
|
"""Запускает браузер с профилем аккаунта и подставляет cookies сессии."""
|
||||||
p = sync_playwright().start()
|
|
||||||
kwargs = dict(
|
kwargs = dict(
|
||||||
user_data_dir=os.path.join(account_dir, "profile"),
|
user_data_dir=os.path.join(account_dir, "profile"),
|
||||||
headless=is_headless(),
|
headless=is_headless(),
|
||||||
@@ -127,6 +127,15 @@ def _launch_context(account_dir):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
def _context_alive(ctx):
|
||||||
|
"""Проверяет, жив ли контекст (лёгкая операция, не создаёт страниц)."""
|
||||||
|
try:
|
||||||
|
ctx.pages
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _inject_session_cookies(context, account_dir):
|
def _inject_session_cookies(context, account_dir):
|
||||||
"""Подставляет cookies из session.txt — вход на hh.ru без ручного логина."""
|
"""Подставляет cookies из session.txt — вход на hh.ru без ручного логина."""
|
||||||
path = os.path.join(account_dir, "session.txt")
|
path = os.path.join(account_dir, "session.txt")
|
||||||
@@ -149,10 +158,33 @@ def _inject_session_cookies(context, account_dir):
|
|||||||
|
|
||||||
|
|
||||||
def _get_context(account_dir, user, account):
|
def _get_context(account_dir, user, account):
|
||||||
|
"""Возвращает живой контекст; если браузер умер — перезапускает."""
|
||||||
key = (user, account)
|
key = (user, account)
|
||||||
if key not in _browsers:
|
ctx = _browsers.get(key)
|
||||||
_browsers[key] = _launch_context(account_dir)
|
if ctx is None or not _context_alive(ctx):
|
||||||
return _browsers[key]
|
if ctx is not None:
|
||||||
|
print(f" ⚠ Браузер {key} закрыт — перезапускаю")
|
||||||
|
_close_context(key)
|
||||||
|
p = sync_playwright().start()
|
||||||
|
try:
|
||||||
|
ctx = _launch_context_with(p, account_dir)
|
||||||
|
except Exception:
|
||||||
|
p.stop()
|
||||||
|
raise
|
||||||
|
_browsers[key] = ctx
|
||||||
|
_playwrights[key] = p
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
|
def _new_page(account_dir, user, account):
|
||||||
|
"""Создаёт страницу; если контекст умер между проверкой и созданием —
|
||||||
|
перезапускает браузер и пробует ещё раз."""
|
||||||
|
try:
|
||||||
|
return _get_context(account_dir, user, account).new_page()
|
||||||
|
except Exception:
|
||||||
|
print(f" ⚠ Не удалось создать страницу для {(user, account)} — перезапускаю браузер")
|
||||||
|
_close_context((user, account))
|
||||||
|
return _get_context(account_dir, user, account).new_page()
|
||||||
|
|
||||||
|
|
||||||
def _save_screenshot(page, screenshot_dir, vid):
|
def _save_screenshot(page, screenshot_dir, vid):
|
||||||
@@ -181,8 +213,7 @@ def open_response_page(account_dir, user, account, vacancy, message,
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
context = _get_context(account_dir, user, account)
|
page = _new_page(account_dir, user, account)
|
||||||
page = context.new_page()
|
|
||||||
_pages[key] = page
|
_pages[key] = page
|
||||||
page.goto(vacancy["url"], timeout=45000)
|
page.goto(vacancy["url"], timeout=45000)
|
||||||
page.wait_for_timeout(2500)
|
page.wait_for_timeout(2500)
|
||||||
@@ -276,15 +307,14 @@ def confirm_send(user, account, vid):
|
|||||||
def open_login_page(account_dir, user, account):
|
def open_login_page(account_dir, user, account):
|
||||||
"""Открывает hh.ru в браузере (для ручного входа, локальный режим с экраном)."""
|
"""Открывает hh.ru в браузере (для ручного входа, локальный режим с экраном)."""
|
||||||
key = (user, account)
|
key = (user, account)
|
||||||
context = _get_context(account_dir, user, account)
|
page = _new_page(account_dir, user, account)
|
||||||
page = context.new_page()
|
|
||||||
_pages[key] = page
|
_pages[key] = page
|
||||||
page.goto("https://hh.ru", timeout=45000)
|
page.goto("https://hh.ru", timeout=45000)
|
||||||
return True, "Страница hh.ru открыта"
|
return True, "Страница hh.ru открыта"
|
||||||
|
|
||||||
|
|
||||||
def close_context(user, account):
|
def _close_context(key):
|
||||||
key = (user, account)
|
"""Закрывает контекст и останавливает playwright-инстанс (из браузерного потока)."""
|
||||||
if key in _browsers:
|
if key in _browsers:
|
||||||
try:
|
try:
|
||||||
_browsers[key].close()
|
_browsers[key].close()
|
||||||
@@ -292,6 +322,16 @@ def close_context(user, account):
|
|||||||
pass
|
pass
|
||||||
_browsers.pop(key, None)
|
_browsers.pop(key, None)
|
||||||
_pages.pop(key, None)
|
_pages.pop(key, None)
|
||||||
|
p = _playwrights.pop(key, None)
|
||||||
|
if p:
|
||||||
|
try:
|
||||||
|
p.stop()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def close_context(user, account):
|
||||||
|
_close_context((user, account))
|
||||||
|
|
||||||
|
|
||||||
# ---------- Публичные обёртки (можно вызывать из любого потока) ----------
|
# ---------- Публичные обёртки (можно вызывать из любого потока) ----------
|
||||||
|
|||||||
Reference in new issue
Block a user