任务 ID: task-wf027  |  文件: session.md  |  最后修改: 2026-02-23 23:50:27

task-wf027 session.md — web_fetch.py Playwright JS 渲染支持

Ai.Dev 执行日志

依赖安装

playwright 安装:

pip install playwright --break-system-packages
→ Successfully installed greenlet-3.3.2 playwright-1.58.0 pyee-13.0.1

Chromium 下载:

playwright install chromium
→ Chrome for Testing 145.0.7632.6 (playwright chromium v1208) downloaded to /root/.cache/ms-playwright/chromium-1208
→ Chrome Headless Shell 145.0.7632.6 (playwright chromium-headless-shell v1208) downloaded to /root/.cache/ms-playwright/chromium_headless_shell-1208
→ FFmpeg (playwright ffmpeg v1011) downloaded to /root/.cache/ms-playwright/ffmpeg-1011

代码修改

修改文件:/root/.openclaw/workspace/scripts/web_fetch.py

新增内容:
1. --js flag(argparse)
2. fetch_with_playwright(url, proxy=None, timeout=30) 函数:
- launch(headless=True, args=["--no-sandbox", "--disable-setuid-sandbox"]) — root 必须
- browser.new_context(proxy={"server": proxy}) — Context 级别代理
- page.goto(url, wait_until="networkidle") — 等待 JS 渲染完成
- with sync_playwright() 确保资源按需启动/关闭
3. main() 中按 --js 分支:有 --proxy 直接轮换,无 --proxy 先直连再 fallback 代理
4. 无 --js 时:现有 requests 逻辑完全不变

测试验证

测试1:静态页面(无 --js),验证现有功能不受影响

python3 web_fetch.py https://example.com --mode text --max-chars 500

结果:

# Example Domain

Example Domain This domain is for use in documentation examples without needing permission. Avoid use in operations. Learn more
EXIT:0

✅ 通过

测试2:--js 直连渲染

python3 web_fetch.py https://example.com --js --mode text --max-chars 500

结果:

[js+direct]
# Example Domain

Example Domain This domain is for use in documentation examples without needing permission. Avoid use in operations. Learn more
EXIT:0

✅ 通过

测试3:--js + --proxy 组合

python3 web_fetch.py https://example.com --js --proxy --mode text --max-chars 500

结果:

[js+proxy via socks5://127.0.0.1:50003]
# Example Domain

Example Domain This domain is for use in documentation examples without needing permission. Avoid use in operations. Learn more
EXIT:0

✅ 通过

4种组合覆盖情况

组合 行为 状态
无 --js,无 --proxy requests 直连 → fallback 代理 ✅ 不变
无 --js,有 --proxy requests 代理轮换 ✅ 不变
有 --js,无 --proxy Playwright 直连 → fallback 代理 ✅ 新增
有 --js,有 --proxy Playwright 代理轮换 ✅ 新增

Ai.Rev 审计报告

审计时间: 2026-02-23
审计人: Ai.Rev

审计方法

  1. 阅读 session.md 中 coder 的执行日志
  2. 直接读取修改后的 web_fetch.py 源码
  3. 实际运行验证(4种组合 + 错误处理)

审计要点逐项核查

要点1:4种组合(--js × --proxy)是否都正确处理

组合 代码路径 实测结果
无js 无proxy 直连→fallback代理(requests) ✅ 通过
无js 有proxy 代理轮换(requests) ✅ 通过(代码逻辑正确)
有js 无proxy 直连→fallback代理(Playwright) ✅ 实测通过
有js 有proxy 代理轮换(Playwright) ✅ 实测通过

要点2:--no-sandbox 是否传给 Playwright

✅ 代码第55-56行:launch_args = ["--no-sandbox", "--disable-setuid-sandbox"],且 p.chromium.launch(headless=True, args=launch_args)。额外加了 --disable-setuid-sandbox,对 root 运行更稳健。

要点3:代理是否用 Context 级别传入

✅ 代码第58-61行:context_options["proxy"] = {"server": proxy},通过 browser.new_context(**context_options) 传入,不是 Browser 级别。完全符合要求。

要点4:现有 requests 逻辑是否完好

--js 分支完全独立,非 --js 路径代码未变动。实测静态页面通过。

要点5:异常处理是否合理

✅ 实测错误场景(无效域名):
- RC=1,STDOUT 为空,错误信息写入 STDERR
- 错误信息包含具体的失败原因(每个代理各自的异常)
- 用户可通过 RC 和 STDERR 判断失败原因

fetch_with_playwright 内部使用 try/finally 确保 browser.close() 必定执行;外层 with sync_playwright() 确保 Playwright 进程清理。资源管理正确。

要点6:依赖安装方式

pip install playwright --break-system-packages 与现有脚本环境一致(其他依赖如 readability 也用此方式)。Chromium 安装到 ~/.cache/ms-playwright/,不污染系统包。

发现的轻微问题

[低] [js+direct] / [js+proxy via ...] 状态信息写入 STDERR,与原有 requests 的 [via proxy] 风格一致,无问题。但 session.md 测试输出中这些信息显示在内容前面,实际上它们是 STDERR——这只是 coder 合并 stderr+stdout 展示的记录方式,代码本身无误。

[低] --proxy 模式下(有--js),若所有代理均失败,错误信息写到了 sys.stderr,但代码写的是:

print(f"ERROR: Playwright 所有方式均失败\n" + "\n".join(errors), file=sys.stderr)

实测确认 STDOUT 为空,STDERR 有错误信息,RC=1。✅ 正确。

审计结论

通过,无需修改。

代码实现质量良好:
- 4种组合全部正确处理
- 关键参数(--no-sandbox、Context级别代理)均正确
- 资源管理(browser/playwright 生命周期)正确
- 错误处理规范(RC=1,STDERR输出,STDOUT为空)
- 现有功能完全不受影响