fix: 自动绕过 SSL 证书过期的拦截页面
导出下载链接的 SSL 证书过期,Chrome 会弹出"您的连接不是私密连接"页面 阻止下载。新增 _bypass_ssl_interstitial() 方法,自动点击「高级」→「继续前往」 绕过 SSL 拦截,并在下载事件超时后做二次 SSL 绕过尝试。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+86
-2
@@ -196,13 +196,16 @@ class SecsionDownloader:
|
||||
|
||||
# 点击导出报表并捕获下载
|
||||
logger.info("点击导出报表...")
|
||||
download_timeout = 120000 # 2 分钟
|
||||
download_timeout = 120000 # 2 分钟,给 SSL 绕过留足时间
|
||||
|
||||
try:
|
||||
async with page.expect_download(timeout=download_timeout) as download_info:
|
||||
await export_btn.click()
|
||||
logger.info("等待文件下载中...")
|
||||
|
||||
# 点击导出后,可能弹出 SSL 证书过期拦截页面
|
||||
await self._bypass_ssl_interstitial(page)
|
||||
|
||||
download = await download_info.value
|
||||
filename = download.suggested_filename
|
||||
save_path = os.path.join(self.download_dir, filename)
|
||||
@@ -211,8 +214,25 @@ class SecsionDownloader:
|
||||
return save_path
|
||||
|
||||
except Exception as download_err:
|
||||
# Playwright download 事件未触发,尝试文件系统兜底检测
|
||||
# Playwright download 事件未触发,尝试 SSL 绕过后再等
|
||||
logger.warning(f"Playwright 下载事件捕获失败: {download_err}")
|
||||
|
||||
# 二次尝试:可能 SSL 页面刚出现,再尝试绕过
|
||||
bypassed = await self._bypass_ssl_interstitial(page)
|
||||
if bypassed:
|
||||
logger.info("SSL 拦截已绕过,等待下载...")
|
||||
try:
|
||||
async with page.expect_download(timeout=30000) as dl_info:
|
||||
pass
|
||||
download = await dl_info.value
|
||||
filename = download.suggested_filename
|
||||
save_path = os.path.join(self.download_dir, filename)
|
||||
await download.save_as(save_path)
|
||||
logger.info(f"SSL 绕过后下载成功: {save_path}")
|
||||
return save_path
|
||||
except Exception:
|
||||
logger.warning("SSL 绕过后仍未触发下载事件")
|
||||
|
||||
logger.info("尝试文件系统兜底检测...")
|
||||
|
||||
# 等待一小段时间让可能的下载完成
|
||||
@@ -290,6 +310,70 @@ class SecsionDownloader:
|
||||
# 过滤掉临时文件和调试截图
|
||||
return [f for f in new_files if not f.endswith(('.crdownload', '.tmp')) and not f.startswith('debug_')]
|
||||
|
||||
async def _bypass_ssl_interstitial(self, page):
|
||||
"""
|
||||
绕过 Chrome SSL 证书错误拦截页面
|
||||
|
||||
secsion.com 的导出下载链接 SSL 证书过期,Chrome 会弹
|
||||
"您的连接不是私密连接" 警告页。点 "高级" → "继续前往"。
|
||||
|
||||
Returns:
|
||||
bool: 是否成功绕过(或无需绕过)
|
||||
"""
|
||||
try:
|
||||
await page.wait_for_timeout(2000)
|
||||
current_url = page.url
|
||||
logger.debug(f"SSL 绕过检查: 当前 URL={current_url}")
|
||||
|
||||
# 检查是否在 SSL 错误页面
|
||||
is_ssl_error_page = (
|
||||
'chrome-error' in current_url or
|
||||
'security' in current_url.lower() or
|
||||
await page.evaluate(
|
||||
"""() => {
|
||||
return document.querySelector('#details-button') !== null ||
|
||||
document.querySelector('#proceed-link') !== null ||
|
||||
document.body?.innerText?.includes('您的连接不是私密连接') ||
|
||||
document.body?.innerText?.includes('NET::ERR_CERT');
|
||||
}"""
|
||||
)
|
||||
)
|
||||
|
||||
if not is_ssl_error_page:
|
||||
return False
|
||||
|
||||
logger.info("检测到 SSL 证书错误拦截页面,尝试绕过...")
|
||||
|
||||
# 点击 "高级" 按钮展开详情
|
||||
details_btn = page.locator('#details-button')
|
||||
if await details_btn.count() > 0:
|
||||
await details_btn.click()
|
||||
await page.wait_for_timeout(500)
|
||||
logger.info("已点击「高级」")
|
||||
|
||||
# 点击 "继续前往 xxx(不安全)"
|
||||
proceed_link = page.locator('#proceed-link')
|
||||
if await proceed_link.count() > 0:
|
||||
await proceed_link.click()
|
||||
await page.wait_for_timeout(2000)
|
||||
logger.info("已点击「继续前往(不安全)」,SSL 绕过成功")
|
||||
return True
|
||||
|
||||
# 备选:中文按钮文字
|
||||
unsafe_link = page.get_by_text('继续前往')
|
||||
if await unsafe_link.count() > 0:
|
||||
await unsafe_link.click()
|
||||
await page.wait_for_timeout(2000)
|
||||
logger.info("已点击「继续前往」,SSL 绕过成功")
|
||||
return True
|
||||
|
||||
logger.warning("SSL 拦截页面检测到但未找到绕过按钮")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"SSL 绕过检查异常: {e}")
|
||||
return False
|
||||
|
||||
async def _set_date(self, page, input_box, date_str):
|
||||
"""
|
||||
设置 TDesign 日期选择器的值
|
||||
|
||||
Reference in New Issue
Block a user