diff --git a/browser_login/login.py b/browser_login/login.py index 457983c..c30dadd 100644 --- a/browser_login/login.py +++ b/browser_login/login.py @@ -58,40 +58,56 @@ def patch_drission_ws_handshake() -> None: base_kwargs["http_no_proxy"] = no_proxy_hosts base_kwargs["http_proxy_host"] = None base_kwargs["http_proxy_port"] = None + + # Chrome 149 严格校验,如果 suppress_origin=True 且没有合法 Host/Origin,会被 404 + # 我们这里准备几个干净的 base_kwargs 变体 + clean_kwargs = dict(base_kwargs) + clean_kwargs.pop("suppress_origin", None) candidate_kwargs = [ - # 策略1:最干净的连接,什么都不加 (根据测试脚本,测试 1 成功) + # 策略1:最干净的连接,强制关闭 suppress_origin { - **base_kwargs, + **clean_kwargs, + "suppress_origin": False }, - # 策略2:DrissionPage 默认的抑制 Origin (根据测试脚本,测试 2 成功) + # 策略2:带上明确的 Origin { - **base_kwargs, - "suppress_origin": True - }, - # 策略3:加上 localhost Origin - { - **base_kwargs, + **clean_kwargs, "suppress_origin": False, - "header": ["Origin: http://localhost"] + "header": ["Origin: http://127.0.0.1"] + }, + # 策略3:尊重原始调用的参数(DrissionPage 默认行为) + { + **base_kwargs } ] last_err = None - for candidate in candidate_kwargs: + for i, candidate in enumerate(candidate_kwargs, 1): try: # 强制使用 127.0.0.1,因为在 Docker 内 localhost 可能解析异常 target_url = address.replace("localhost", "127.0.0.1") + log("INFO", f"[DEBUG-WS] 尝试策略 {i} 连接 {target_url} (suppress_origin={candidate.get('suppress_origin')}, header={candidate.get('header')})") return raw_ws_create_connection(target_url, **candidate) except WebSocketBadStatusException as ws_err: + log("WARN", f"[DEBUG-WS] 策略 {i} 失败 (WebSocketBadStatusException): {ws_err}") last_err = ws_err except Exception as other_err: + log("WARN", f"[DEBUG-WS] 策略 {i} 失败 (Exception): {other_err}") last_err = other_err break raise last_err if is_linux_env(): + # 如果是老版的 create_connection,替换它 dp_driver_module.create_connection = resilient_create_connection + # 覆盖 websocket-client 原生的 create_connection 避免某些模块直接引用 + import websocket + websocket.create_connection = resilient_create_connection + # 还有可能是通过导入的方式,比如 from websocket import create_connection + # 我们需要在 driver.py 中覆盖它 + import DrissionPage._base.driver as driver_mod + driver_mod.create_connection = resilient_create_connection dp_driver_module._DTSK_WS_PATCHED = True @@ -307,11 +323,13 @@ def get_page(headless: bool = False, port: int = 9222) -> ChromiumPage: # #endregion try: + log("INFO", f"[DEBUG] 准备实例化 ChromiumPage, address={opt.address}, local_port={opt.local_port}") page = ChromiumPage(opt) + log("OK", "[DEBUG] ChromiumPage 实例化成功!") return page except Exception as e: actual_address = opt.address or f"127.0.0.1:{port}" - log("WARN", f"[DEBUG] ChromiumPage 初始化失败: {e}") + log("WARN", f"[DEBUG] ChromiumPage 初始化失败 (第一次尝试): {e}") devtools_payload = probe_devtools_endpoints(actual_address, log_output=True) if opt.address else {"version": None, "list": None} fallback_page_ws = get_first_page_ws_address(devtools_payload) @@ -350,10 +368,10 @@ def get_page(headless: bool = False, port: int = 9222) -> ChromiumPage: new_co.set_browser_path(browser_path) page = ChromiumPage(new_co) - log("OK", "[DEBUG] 清理后重试成功!") + log("OK", "[DEBUG] 清理后重试实例化 ChromiumPage 成功!") return page except Exception as retry_e: - log("ERR", f"[DEBUG] 清理后重试依然失败: {retry_e}") + log("ERR", f"[DEBUG] 清理后重试依然失败 (第二次尝试): {retry_e}") e = retry_e # #region debug-point B:devtools-http-probe