From 82ff8a261007d8d1d7ebf35f47803da0266d234d Mon Sep 17 00:00:00 2001 From: hz4th_coder Date: Mon, 31 Aug 2026 16:56:22 +0800 Subject: [PATCH] =?UTF-8?q?v1.7.3=20=E5=89=8D=E7=AB=AF=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E9=80=89=E6=8B=A9=E5=99=A8=20+=20=E6=9A=B4?= =?UTF-8?q?=E9=9C=B2=20/api/backends=20+=20=E6=8E=A5=E9=80=9A=20chrome-cdp?= =?UTF-8?q?=20=E5=90=8E=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 GET /api/backends 返回可用后端/默认顺序/说明,供前端动态渲染 - 前端表单新增「捕获后端」下拉框: 自动(默认,优先agent-browser超时自动回退playwright)/agent-browser/playwright/chrome-cdp,不可用的置灰 - 接通 chrome-cdp 后端: 之前函数已实现但 capture_webpage 未接线(传 backend=chrome-cdp 会报 Unknown backend),现支持 screenshot/html/text,连接已开的 Chrome(--remote-debugging-port) - 前端 currentData 携带 backend 字段 --- app.py | 75 ++++++++++++++++++++++++++++++++++++++++++-- templates/index.html | 36 ++++++++++++++++++++- 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/app.py b/app.py index 08b4435..ed517d7 100644 --- a/app.py +++ b/app.py @@ -708,6 +708,14 @@ async def capture_with_cdp( "html": html }) + elif action == "text": + text = await page.evaluate("document.body.innerText") + result_data.append({ + "url": current_url, + "title": title, + "text": clean_text(text) + }) + if result_data: return {"success": True, "pages": result_data} else: @@ -983,13 +991,14 @@ def capture_webpage( full_page: bool = False, viewport: dict = None, wait_time: int = 2000, - backend: str = "auto" + backend: str = "auto", + cdp_port: int = 9222 ): """ 捕获网页 Args: - backend: "auto", "agent-browser", "playwright" + backend: "auto", "agent-browser", "playwright", "chrome-cdp" """ # 选择后端 if backend == "auto": @@ -1044,6 +1053,33 @@ def capture_webpage( except Exception as e: return {"success": False, "error": str(e)} + elif backend == "chrome-cdp": + # 连接已打开的 Chrome(需 --remote-debugging-port=),归一化为单结果 + if not PLAYWRIGHT_AVAILABLE: + return {"success": False, "error": "Playwright not installed (chrome-cdp 依赖 playwright)"} + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + r = loop.run_until_complete( + capture_with_cdp(url_hint=url, action=action, cdp_port=cdp_port) + ) + loop.close() + except Exception as e: + return {"success": False, "error": str(e)} + if not r.get("success"): + return r + if not r.get("pages"): + return {"success": False, "error": "chrome-cdp 未找到匹配页面(请确认 Chrome 已开 --remote-debugging-port)"} + p0 = r["pages"][0] + res = {"success": True, "title": p0.get("title", ""), "backend": "chrome-cdp"} + if action == "screenshot": + res["file_path"] = p0["file_path"] + elif action == "html": + res["html"] = p0["html"] + elif action == "text": + res["text"] = p0["text"] + return res + else: return {"success": False, "error": f"Unknown backend: {backend}"} @@ -1054,6 +1090,37 @@ def index(): return render_template('index.html') +@app.route('/api/backends') +def api_backends(): + """返回可用后端及默认选择顺序,供前端渲染选择器""" + available = [] + if AGENT_BROWSER_AVAILABLE: + available.append("agent-browser") + if PLAYWRIGHT_AVAILABLE: + available.append("playwright") + # chrome-cdp 依赖 playwright 的 connect_over_cdp + available.append("chrome-cdp") + unavailable = [] + if not AGENT_BROWSER_AVAILABLE: + unavailable.append("agent-browser") + if not PLAYWRIGHT_AVAILABLE: + unavailable.append("playwright") + unavailable.append("chrome-cdp") + return jsonify({ + "success": True, + "default": "auto", + "available": available, + "unavailable": unavailable, + "priority": available + unavailable, + "labels": { + "auto": "自动(推荐:优先 agent-browser,打开超时自动回退 Playwright)", + "agent-browser": "agent-browser(Rust 版,反爬强、快,默认首选)", + "playwright": "Playwright(Python 版,domcontentloaded 更宽容,慢页面更稳)", + "chrome-cdp": "Chrome CDP(连接已打开的 Chrome,需 --remote-debugging-port 启动)" + } + }) + + @app.route('/api') def api_info(): """API信息""" @@ -1066,6 +1133,7 @@ def api_info(): }, "endpoints": { "/api/capture": "POST - Capture webpage (screenshot/html/text/smart) + 自动入库历史(记录调用者/方式/原始HTML)", + "/api/backends": "GET - 可用后端列表与默认顺序(供前端渲染后端选择器)", "/api/history": "GET - 历史记录分页列表 (?page&page_size&action&search)", "/api/history/": "GET - 历史记录详情 / DELETE - 删除记录", "/api/history//file": "GET - 读取历史截图文件", @@ -1320,7 +1388,8 @@ def capture(): full_page=full_page, viewport=viewport, wait_time=wait_time, - backend=backend + backend=backend, + cdp_port=cdp_port ) backend_used = result.get("backend", backend) diff --git a/templates/index.html b/templates/index.html index c35b245..363fc78 100644 --- a/templates/index.html +++ b/templates/index.html @@ -624,6 +624,16 @@ +
+ + + + 自动 = 优先 agent-browser,打开页面超时自动回退 Playwright(推荐) + +
+
@@ -844,12 +854,35 @@ DELETE /api/history/<id> // 删除历史 let smartConfig = null; let lastSmartHistoryId = null; - // 页面加载时读取历史 + 智能配置 + // 页面加载时读取历史 + 智能配置 + 可用后端 window.addEventListener('load', () => { loadHistory(1); loadSmartConfig(); + loadBackends(); }); + async function loadBackends() { + const sel = document.getElementById('backend'); + const hint = document.getElementById('backendHint'); + try { + const res = await fetch('/api/backends'); + const data = await res.json(); + if (!data.success) throw new Error(data.error || '加载失败'); + const labels = data.labels || {}; + const opts = ['auto'].concat(data.available || []); + sel.innerHTML = opts.map(v => + `` + ).join('') + (data.unavailable || []).map(v => + `` + ).join(''); + sel.value = 'auto'; + hint.textContent = labels['auto'] || hint.textContent; + } catch (err) { + sel.innerHTML = ''; + hint.textContent = '后端列表加载失败:' + err.message; + } + } + // 操作类型切换:智能模式显示提示条 document.querySelectorAll('input[name="action"]').forEach(r => { r.addEventListener('change', () => { @@ -957,6 +990,7 @@ DELETE /api/history/<id> // 删除历史 scroll_delay: scrollDelay, full_page: fullPage, wait_time: waitTime, + backend: document.getElementById('backend').value || 'auto', viewport: { width: viewportWidth, height: viewportHeight