fix(bili): 兼容首页新版 header 的登录入口选择器

B 站首页 header 新旧两套实现并行灰度,登录入口选择器不同:
- 新版 header:.header-avatar-unlogin-entry
- 旧版 header:.right-entry__outside.go-login-btn 内的 .header-login-entry

原代码只匹配旧版,且 header 版本按 cookie 分流(携带过期 SESSDATA 也会
被分到新版),导致未登录场景下点击登录按钮直接 30s 超时。

改为组合选择器,并在等待超时时输出明确的排查提示,替代原先裸的
Playwright traceback。两套 header 的按钮均调用同一个 openMiniLogin(),
弹窗及二维码结构未变,故无需改动。
This commit is contained in:
程序员阿江(Relakkes)
2026-09-18 14:44:26 +08:00
parent 0ca7b29cf0
commit 9cfc148577

View File

@@ -29,6 +29,7 @@ import sys
from typing import Optional
from playwright.async_api import BrowserContext, Page
from playwright.async_api import TimeoutError as PlaywrightTimeoutError
from tenacity import (RetryError, retry, retry_if_result, stop_after_attempt,
wait_fixed)
@@ -36,6 +37,16 @@ import config
from base.base_crawler import AbstractLogin
from tools import utils
# B 站首页 header 目前新旧两套实现并行灰度,未登录时的登录入口选择器不同:
# - 新版 header.header-avatar-unlogin-entry
# - 旧版 header.right-entry__outside.go-login-btn 内部的 .header-login-entry
# 两者点击后都调用 mini-login-v2 的 openMiniLogin(),拉起同一个登录弹窗,
# 弹窗内的二维码仍是 .login-scan-box 里的 img所以这里只需要兼容入口按钮。
LOGIN_ENTRY_SELECTORS = [
".header-avatar-unlogin-entry",
".right-entry__outside.go-login-btn .header-login-entry",
]
class BilibiliLogin(AbstractLogin):
def __init__(self,
@@ -82,10 +93,22 @@ class BilibiliLogin(AbstractLogin):
utils.logger.info("[BilibiliLogin.login_by_qrcode] Begin login bilibili by qrcode ...")
# click login button
login_button_ele = self.context_page.locator(
"xpath=//div[@class='right-entry__outside go-login-btn']//div"
)
await login_button_ele.click()
login_entry_selector = ", ".join(LOGIN_ENTRY_SELECTORS)
try:
await self.context_page.wait_for_selector(
selector=login_entry_selector,
state="visible",
timeout=30_000,
)
except PlaywrightTimeoutError:
utils.logger.error(
"[BilibiliLogin.login_by_qrcode] Login entry not found on the homepage, "
f"selectors tried: {LOGIN_ENTRY_SELECTORS}. "
"Bilibili may have changed the homepage header again, "
"please update LOGIN_ENTRY_SELECTORS in media_platform/bilibili/login.py."
)
sys.exit()
await self.context_page.locator(login_entry_selector).first.click()
await asyncio.sleep(1)
# find login qrcode
qrcode_img_selector = "//div[@class='login-scan-box']//img"