Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f7f14d7114 | |||
| ef4d26f11b | |||
| fc43e6e806 | |||
| 5783feb60d | |||
| 98ef180878 | |||
| 6b7e856e38 | |||
| ccb8479242 |
43
PROJECT_SUMMARY.md
Normal file
43
PROJECT_SUMMARY.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Web Capture API 项目
|
||||||
|
|
||||||
|
## 待推送信息
|
||||||
|
|
||||||
|
- **仓库地址**: http://121.40.164.32:12007/hz4th_coder/web-capture-api.git
|
||||||
|
- **当前版本**: v1.0.2
|
||||||
|
- **提交历史**:
|
||||||
|
- v1.0.0: 初始化项目
|
||||||
|
- v1.0.1: 修改端口为 16025
|
||||||
|
- v1.0.2: 修复 agent-browser socket 权限问题
|
||||||
|
|
||||||
|
## 操作步骤
|
||||||
|
|
||||||
|
### 1. 在 Git 平台创建仓库
|
||||||
|
|
||||||
|
请大爷登录 Git 平台 (http://121.40.164.32:12007) 并创建仓库:
|
||||||
|
- 仓库名称: `web-capture-api`
|
||||||
|
- 所属用户: `hz4th_coder`
|
||||||
|
- 可见性: 公开或私有(根据需要)
|
||||||
|
|
||||||
|
### 2. 推送代码
|
||||||
|
|
||||||
|
仓库创建后,运行以下命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/openclaw/.openclaw/workspace-hz4th_coder/works/web-capture-api
|
||||||
|
git push -u origin master --tags
|
||||||
|
```
|
||||||
|
|
||||||
|
## 项目功能
|
||||||
|
|
||||||
|
- 网页截图(全页或视口)
|
||||||
|
- HTML 代码提取
|
||||||
|
- 自动滚动加载动态内容
|
||||||
|
- 自定义视口大小
|
||||||
|
- Web 界面和 RESTful API
|
||||||
|
- 支持 agent-browser 和 Playwright 双后端
|
||||||
|
- Docker 部署支持
|
||||||
|
|
||||||
|
## 服务地址
|
||||||
|
|
||||||
|
- Web 界面: http://192.168.0.101:16025
|
||||||
|
- API 接口: http://192.168.0.101:16025/api/capture
|
||||||
206
app.py
206
app.py
@@ -20,8 +20,17 @@ from datetime import datetime
|
|||||||
try:
|
try:
|
||||||
from playwright.async_api import async_playwright
|
from playwright.async_api import async_playwright
|
||||||
PLAYWRIGHT_AVAILABLE = True
|
PLAYWRIGHT_AVAILABLE = True
|
||||||
|
# 尝试导入 stealth 反检测
|
||||||
|
try:
|
||||||
|
from playwright_stealth import stealth_async
|
||||||
|
STEALTH_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
STEALTH_AVAILABLE = False
|
||||||
|
print("⚠️ playwright-stealth 未安装,反爬虫能力降低")
|
||||||
except ImportError:
|
except ImportError:
|
||||||
PLAYWRIGHT_AVAILABLE = False
|
PLAYWRIGHT_AVAILABLE = False
|
||||||
|
STEALTH_AVAILABLE = False
|
||||||
|
print("⚠️ Playwright 未安装,请运行: pip install playwright && playwright install chromium")
|
||||||
|
|
||||||
# 检查 agent-browser 是否可用
|
# 检查 agent-browser 是否可用
|
||||||
def check_agent_browser():
|
def check_agent_browser():
|
||||||
@@ -44,6 +53,10 @@ CORS(app)
|
|||||||
CAPTURE_DIR = Path(tempfile.gettempdir()) / "web_captures"
|
CAPTURE_DIR = Path(tempfile.gettempdir()) / "web_captures"
|
||||||
CAPTURE_DIR.mkdir(exist_ok=True)
|
CAPTURE_DIR.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
# agent-browser socket 目录
|
||||||
|
os.environ.setdefault("AGENT_BROWSER_SOCKET_DIR", "/tmp/agent-browser-sockets")
|
||||||
|
Path(os.environ["AGENT_BROWSER_SOCKET_DIR"]).mkdir(exist_ok=True, parents=True)
|
||||||
|
|
||||||
|
|
||||||
class AgentBrowserSession:
|
class AgentBrowserSession:
|
||||||
"""agent-browser 会话管理器"""
|
"""agent-browser 会话管理器"""
|
||||||
@@ -55,12 +68,17 @@ class AgentBrowserSession:
|
|||||||
def run(self, cmd, timeout=60000):
|
def run(self, cmd, timeout=60000):
|
||||||
"""执行 agent-browser 命令"""
|
"""执行 agent-browser 命令"""
|
||||||
full_cmd = self.base_cmd + cmd
|
full_cmd = self.base_cmd + cmd
|
||||||
|
# 设置必要的环境变量
|
||||||
|
env = os.environ.copy()
|
||||||
|
env['AGENT_BROWSER_SOCKET_DIR'] = '/tmp/agent-browser-sockets'
|
||||||
|
env['AGENT_BROWSER_USER_AGENT'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
full_cmd,
|
full_cmd,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=timeout / 1000
|
timeout=timeout / 1000,
|
||||||
|
env=env
|
||||||
)
|
)
|
||||||
return result.returncode == 0, result.stdout, result.stderr
|
return result.returncode == 0, result.stdout, result.stderr
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
@@ -70,6 +88,9 @@ class AgentBrowserSession:
|
|||||||
|
|
||||||
def open(self, url):
|
def open(self, url):
|
||||||
"""打开网页"""
|
"""打开网页"""
|
||||||
|
# 添加反爬虫检测的 User-Agent
|
||||||
|
env = os.environ.copy()
|
||||||
|
env['AGENT_BROWSER_USER_AGENT'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
||||||
return self.run(["open", url], timeout=30000)
|
return self.run(["open", url], timeout=30000)
|
||||||
|
|
||||||
def set_viewport(self, width, height):
|
def set_viewport(self, width, height):
|
||||||
@@ -130,6 +151,9 @@ def capture_with_agent_browser(
|
|||||||
# 打开网页
|
# 打开网页
|
||||||
success, stdout, stderr = session.open(url)
|
success, stdout, stderr = session.open(url)
|
||||||
if not success:
|
if not success:
|
||||||
|
# 检查是否是反爬虫拦截
|
||||||
|
if "403" in stdout or "Access Denied" in stdout:
|
||||||
|
return {"success": False, "error": "网站反爬虫拦截 (403),建议使用 Playwright 后端或手动添加请求头"}
|
||||||
return {"success": False, "error": f"Failed to open URL: {stderr}"}
|
return {"success": False, "error": f"Failed to open URL: {stderr}"}
|
||||||
|
|
||||||
# 等待页面加载
|
# 等待页面加载
|
||||||
@@ -179,36 +203,140 @@ async def capture_with_playwright(
|
|||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
使用 Playwright 捕获网页
|
使用 Playwright 捕获网页
|
||||||
|
Playwright 有更强的反爬虫能力
|
||||||
"""
|
"""
|
||||||
if not PLAYWRIGHT_AVAILABLE:
|
if not PLAYWRIGHT_AVAILABLE:
|
||||||
return {"success": False, "error": "Playwright not installed"}
|
return {"success": False, "error": "Playwright not installed. Run: pip install playwright && playwright install chromium"}
|
||||||
|
|
||||||
session_id = str(uuid.uuid4())[:8]
|
session_id = str(uuid.uuid4())[:8]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with async_playwright() as p:
|
async with async_playwright() as p:
|
||||||
browser = await p.chromium.launch(headless=True)
|
# 启动浏览器,添加反检测配置
|
||||||
|
# 尝试使用真实 Chrome(如果安装了)
|
||||||
|
try:
|
||||||
|
# 首先尝试使用真实 Chrome 浏览器
|
||||||
|
browser = await p.chromium.launch(
|
||||||
|
headless=True,
|
||||||
|
channel='chrome', # 使用真实 Chrome 而不是 Chromium
|
||||||
|
args=[
|
||||||
|
'--disable-blink-features=AutomationControlled',
|
||||||
|
'--disable-dev-shm-usage',
|
||||||
|
'--no-sandbox',
|
||||||
|
'--ignore-certificate-errors',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
# 如果没有 Chrome,使用 Chromium
|
||||||
|
browser = await p.chromium.launch(
|
||||||
|
headless=True,
|
||||||
|
args=[
|
||||||
|
'--disable-blink-features=AutomationControlled',
|
||||||
|
'--disable-dev-shm-usage',
|
||||||
|
'--no-sandbox',
|
||||||
|
'--ignore-certificate-errors',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
viewport_settings = viewport or {"width": 1920, "height": 1080}
|
viewport_settings = viewport or {"width": 1920, "height": 1080}
|
||||||
|
|
||||||
|
# 创建上下文,模拟真实浏览器
|
||||||
context = await browser.new_context(
|
context = await browser.new_context(
|
||||||
viewport={
|
viewport={
|
||||||
"width": viewport_settings.get("width", 1920),
|
"width": viewport_settings.get("width", 1920),
|
||||||
"height": viewport_settings.get("height", 1080)
|
"height": viewport_settings.get("height", 1080)
|
||||||
},
|
},
|
||||||
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.109 Safari/537.36',
|
||||||
|
# 添加更多浏览器特征
|
||||||
|
locale='en-US',
|
||||||
|
timezone_id='America/New_York',
|
||||||
|
color_scheme='light',
|
||||||
|
has_touch=False,
|
||||||
|
is_mobile=False,
|
||||||
|
java_script_enabled=True,
|
||||||
|
accept_downloads=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 注入反检测脚本
|
||||||
|
await context.add_init_script("""
|
||||||
|
// 移除 webdriver 标记
|
||||||
|
Object.defineProperty(navigator, 'webdriver', {
|
||||||
|
get: () => undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加插件
|
||||||
|
Object.defineProperty(navigator, 'plugins', {
|
||||||
|
get: () => {
|
||||||
|
return [
|
||||||
|
{name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format'},
|
||||||
|
{name: 'Chrome PDF Viewer', filename: 'mhjfbmdgncjokjjpdkfnmlglggk', description: ''},
|
||||||
|
{name: 'Native Client', filename: 'internal-nacl-plugin', description: ''}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 语言列表
|
||||||
|
Object.defineProperty(navigator, 'languages', {
|
||||||
|
get: () => ['en-US', 'en', 'zh-CN']
|
||||||
|
});
|
||||||
|
|
||||||
|
// 平台
|
||||||
|
Object.defineProperty(navigator, 'platform', {
|
||||||
|
get: () => 'Win32'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 硬件并发
|
||||||
|
Object.defineProperty(navigator, 'hardwareConcurrency', {
|
||||||
|
get: () => 8
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设备内存
|
||||||
|
Object.defineProperty(navigator, 'deviceMemory', {
|
||||||
|
get: () => 8
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chrome 对象
|
||||||
|
window.chrome = {
|
||||||
|
runtime: {
|
||||||
|
PlatformOs: {MAC: 'mac', WIN: 'win', ANDROID: 'android', CROS: 'cros', LINUX: 'linux', OPENBSD: 'openbsd'},
|
||||||
|
PlatformArch: {ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64'},
|
||||||
|
PlatformNaclArch: {ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64'},
|
||||||
|
Request: function() {},
|
||||||
|
connect: function() {},
|
||||||
|
sendMessage: function() {}
|
||||||
|
},
|
||||||
|
loadTimes: function() {},
|
||||||
|
csi: function() {},
|
||||||
|
app: {}
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
|
||||||
page = await context.new_page()
|
page = await context.new_page()
|
||||||
|
|
||||||
try:
|
# 应用 stealth 反检测(如果可用)
|
||||||
await page.goto(url, wait_until="networkidle", timeout=30000)
|
if STEALTH_AVAILABLE:
|
||||||
except:
|
await stealth_async(page)
|
||||||
await page.goto(url, wait_until="domcontentloaded", timeout=30000)
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
await page.goto(url, wait_until="domcontentloaded", timeout=30000)
|
||||||
|
except Exception as e:
|
||||||
|
# 如果 domcontentloaded 超时,尝试 commit
|
||||||
|
try:
|
||||||
|
await page.goto(url, wait_until="commit", timeout=30000)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 固定等待时间(用于验证、加载等过程)
|
||||||
if wait_time > 0:
|
if wait_time > 0:
|
||||||
await page.wait_for_timeout(wait_time)
|
await page.wait_for_timeout(wait_time)
|
||||||
|
|
||||||
|
# 等待页面基本稳定
|
||||||
|
try:
|
||||||
|
await page.wait_for_load_state("networkidle", timeout=5000)
|
||||||
|
except:
|
||||||
|
# networkidle 超时没关系,继续执行
|
||||||
|
pass
|
||||||
|
|
||||||
if scroll_times > 0:
|
if scroll_times > 0:
|
||||||
for i in range(scroll_times):
|
for i in range(scroll_times):
|
||||||
await page.evaluate("window.scrollBy(0, 800)")
|
await page.evaluate("window.scrollBy(0, 800)")
|
||||||
@@ -236,6 +364,59 @@ async def capture_with_playwright(
|
|||||||
return {"success": False, "error": str(e)}
|
return {"success": False, "error": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
async def capture_with_cdp(
|
||||||
|
url_hint: str = "",
|
||||||
|
action: str = "screenshot",
|
||||||
|
cdp_port: int = 9222
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
使用 CDP 连接到已打开的 Chrome 浏览器
|
||||||
|
用于方案三:手动验证后自动截图
|
||||||
|
"""
|
||||||
|
if not PLAYWRIGHT_AVAILABLE:
|
||||||
|
return {"success": False, "error": "Playwright not installed"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with async_playwright() as p:
|
||||||
|
# 连接到已运行的 Chrome
|
||||||
|
browser = await p.chromium.connect_over_cdp(f"http://localhost:{cdp_port}")
|
||||||
|
|
||||||
|
# 获取所有页面
|
||||||
|
contexts = browser.contexts
|
||||||
|
result_data = []
|
||||||
|
|
||||||
|
for context in contexts:
|
||||||
|
for page in context.pages:
|
||||||
|
current_url = page.url
|
||||||
|
title = await page.title()
|
||||||
|
|
||||||
|
# 如果 URL 包含提示词或者是唯一页面
|
||||||
|
if url_hint in current_url or not url_hint:
|
||||||
|
if action == "screenshot":
|
||||||
|
temp_file = CAPTURE_DIR / f"cdp_capture_{uuid.uuid4()[:8]}.png"
|
||||||
|
await page.screenshot(path=str(temp_file), full_page=True)
|
||||||
|
result_data.append({
|
||||||
|
"url": current_url,
|
||||||
|
"title": title,
|
||||||
|
"file_path": str(temp_file)
|
||||||
|
})
|
||||||
|
elif action == "html":
|
||||||
|
html = await page.content()
|
||||||
|
result_data.append({
|
||||||
|
"url": current_url,
|
||||||
|
"title": title,
|
||||||
|
"html": html
|
||||||
|
})
|
||||||
|
|
||||||
|
if result_data:
|
||||||
|
return {"success": True, "pages": result_data}
|
||||||
|
else:
|
||||||
|
return {"success": False, "error": "No matching page found"}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {"success": False, "error": f"CDP connection failed: {str(e)}"}
|
||||||
|
|
||||||
|
|
||||||
def capture_webpage(
|
def capture_webpage(
|
||||||
url: str,
|
url: str,
|
||||||
action: str = "screenshot",
|
action: str = "screenshot",
|
||||||
@@ -339,7 +520,8 @@ def capture():
|
|||||||
"full_page": false,
|
"full_page": false,
|
||||||
"viewport": {"width": 1920, "height": 1080},
|
"viewport": {"width": 1920, "height": 1080},
|
||||||
"wait_time": 2000,
|
"wait_time": 2000,
|
||||||
"backend": "auto" | "agent-browser" | "playwright"
|
"backend": "auto" | "agent-browser" | "playwright" | "chrome-cdp",
|
||||||
|
"cdp_port": 9222 // 用于 chrome-cdp 后端,连接到已打开的 Chrome
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
@@ -362,6 +544,8 @@ def capture():
|
|||||||
viewport = data.get("viewport")
|
viewport = data.get("viewport")
|
||||||
wait_time = int(data.get("wait_time", 2000))
|
wait_time = int(data.get("wait_time", 2000))
|
||||||
backend = data.get("backend", "auto")
|
backend = data.get("backend", "auto")
|
||||||
|
cdp_port = int(data.get("cdp_port", 9222))
|
||||||
|
url_hint = data.get("url_hint", "")
|
||||||
|
|
||||||
result = capture_webpage(
|
result = capture_webpage(
|
||||||
url=url,
|
url=url,
|
||||||
@@ -371,7 +555,9 @@ def capture():
|
|||||||
full_page=full_page,
|
full_page=full_page,
|
||||||
viewport=viewport,
|
viewport=viewport,
|
||||||
wait_time=wait_time,
|
wait_time=wait_time,
|
||||||
backend=backend
|
backend=backend,
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if not result["success"]:
|
if not result["success"]:
|
||||||
|
|||||||
69
manual_capture_guide.md
Normal file
69
manual_capture_guide.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# 手动 Chrome 捕获指南
|
||||||
|
|
||||||
|
## 方案三完整流程
|
||||||
|
|
||||||
|
### 1️⃣ 启动真实 Chrome(带调试端口)
|
||||||
|
|
||||||
|
在您的本地机器或服务器上运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux
|
||||||
|
google-chrome --remote-debugging-port=9222 https://www.techpowerup.com/gpu-specs/rtx-pro-6000-blackwell.c4272
|
||||||
|
|
||||||
|
# 或者使用已有的 Chrome 进程(需要先关闭所有 Chrome)
|
||||||
|
google-chrome-stable --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2️⃣ 手动验证(如 Cloudflare)
|
||||||
|
|
||||||
|
- 在浏览器中手动完成验证过程
|
||||||
|
- 等待页面完全加载
|
||||||
|
- 确认可以看到正常内容
|
||||||
|
|
||||||
|
### 3️⃣ 连接到 Chrome 并截图
|
||||||
|
|
||||||
|
验证完成后,运行脚本:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 连接到 Chrome 并截图
|
||||||
|
python3.12 /tmp/connect_chrome.py techpowerup
|
||||||
|
|
||||||
|
# 或者捕获当前所有页面
|
||||||
|
python3.12 /tmp/connect_chrome.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4️⃣ 查看结果
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看截图
|
||||||
|
ls -lh /tmp/chrome_capture_*.png
|
||||||
|
|
||||||
|
# 查看HTML
|
||||||
|
head -100 /tmp/chrome_html_0.html
|
||||||
|
```
|
||||||
|
|
||||||
|
## API 集成方案
|
||||||
|
|
||||||
|
如果需要通过 API 获取内容,可以:
|
||||||
|
|
||||||
|
1. 在服务代码中添加 CDP 连接选项
|
||||||
|
2. 用户先手动验证
|
||||||
|
3. API 连接到已验证的浏览器实例
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
⚠️ **重要**:
|
||||||
|
- Chrome 必须带 `--remote-debugging-port=9222` 启动
|
||||||
|
- 所有 Chrome 进程必须先关闭(或使用新的 user-data-dir)
|
||||||
|
- 验证完成后才能运行脚本
|
||||||
|
- 截图后浏览器保持打开,可以继续使用
|
||||||
|
|
||||||
|
## 一键脚本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 启动 Chrome 并等待用户验证
|
||||||
|
google-chrome --remote-debugging-port=9222 "https://www.techpowerup.com/gpu-specs/rtx-pro-6000-blackwell.c4272" &
|
||||||
|
echo "请在浏览器中完成验证,然后按 Enter 继续截图..."
|
||||||
|
read
|
||||||
|
python3.12 /tmp/connect_chrome.py techpowerup
|
||||||
|
```
|
||||||
@@ -295,20 +295,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>滚动设置(用于加载动态内容)</label>
|
<label>等待设置(重要!)</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div>
|
<div>
|
||||||
<label for="scrollTimes">滚动次数</label>
|
<label for="waitTime">页面加载等待(ms) ⏱️</label>
|
||||||
|
<input type="number" id="waitTime" value="2000" min="0" max="60000" step="500">
|
||||||
|
<small style="color: #666; display: block; margin-top: 4px;">某些网站需要较长验证过程,可设置为 10000-30000</small>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="scrollTimes">滚动次数(可选)</label>
|
||||||
<input type="number" id="scrollTimes" value="0" min="0" max="20">
|
<input type="number" id="scrollTimes" value="0" min="0" max="20">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="scrollDelay">滚动间隔(ms)</label>
|
<label for="scrollDelay">滚动间隔(ms)</label>
|
||||||
<input type="number" id="scrollDelay" value="1000" min="500" max="5000" step="100">
|
<input type="number" id="scrollDelay" value="1000" min="500" max="5000" step="100">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<label for="waitTime">页面加载等待(ms)</label>
|
|
||||||
<input type="number" id="waitTime" value="2000" min="0" max="10000" step="500">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -350,16 +351,23 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card api-docs">
|
<div class="card api-docs">
|
||||||
<h3>📚 API 文档</h3>
|
<h3>📚 使用说明</h3>
|
||||||
<p>POST /api/capture</p>
|
<p><strong>重要提示:</strong></p>
|
||||||
<pre><code>{
|
<ul style="margin: 10px 0; line-height: 1.8;">
|
||||||
|
<li>⏱️ <strong>页面加载等待</strong>:某些网站需要验证过程(如 Cloudflare),请设置较长时间(10000-30000ms)</li>
|
||||||
|
<li>🔄 <strong>滚动次数</strong>:用于加载动态内容(如微博、推特等),建议 3-5 次</li>
|
||||||
|
<li>🎯 <strong>全页截图</strong>:滚动加载后建议开启此选项</li>
|
||||||
|
</ul>
|
||||||
|
<p style="margin-top: 15px;"><strong>API 调用:</strong></p>
|
||||||
|
<pre><code>POST /api/capture
|
||||||
|
{
|
||||||
"url": "https://example.com",
|
"url": "https://example.com",
|
||||||
"action": "screenshot", // 或 "html"
|
"action": "screenshot", // 或 "html"
|
||||||
"scroll_times": 3,
|
"wait_time": 15000, // 重要!验证网站需设置较长等待
|
||||||
"scroll_delay": 1000,
|
"scroll_times": 3, // 可选:加载动态内容
|
||||||
"full_page": true,
|
"scroll_delay": 1000, // 可选:滚动间隔
|
||||||
"viewport": {"width": 1920, "height": 1080},
|
"full_page": true, // 可选:全页截图
|
||||||
"wait_time": 2000
|
"backend": "playwright" // 可选:playwright 或 agent-browser
|
||||||
}</code></pre>
|
}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -405,7 +413,7 @@
|
|||||||
submitBtn.disabled = true;
|
submitBtn.disabled = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('http://localhost:16025/api/capture', {
|
const response = await fetch('/api/capture', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
|
|||||||
Reference in New Issue
Block a user