-
Notifications
You must be signed in to change notification settings - Fork 0
Browser Rendering
当页面内容要 JS 跑完才有时,普通 HTTP 请求拿到的只是一个空壳。浏览器适配器会真的起一个 浏览器、加载页面、等渲染完成,然后把渲染后的 DOM 当作响应体返回。
resp = d.get("https://spa.example.com", adapter="browser")
print(resp.text) # 渲染后的 HTML,不是初始 HTML| 引擎 | 内核 | 反检测 | 浏览器本体 | 什么时候用 |
|---|---|---|---|---|
| camoufox | Firefox | 最彻底(改的是内核层) | 自带一份,约 1 GB | Linux/macOS 默认;对方检测严 |
| patchright | Chromium | 打过补丁的 Playwright | patchright install chromium |
要 Chromium 又要反检测 |
| playwright | 任意三选一 | 无 | playwright install chromium |
行为最可预期;自家页面、调试 |
| DrissionPage | Chromium | 中等(CDP 直连) | 用本机已装的 Chrome | Windows 默认;不想再下浏览器 |
engine = "auto"(默认)按平台选:Windows → DrissionPage,其余 → camoufox。
camoufox 最吃内存。 它是 Firefox 加一整套扩展,单个 context 的开销明显高于 Chromium 系。≤4 GB 内存的机器上把
max_pages设成 1~2,否则会开始换页—— 请求从几秒变几分钟,看起来完全像卡死。
pip install 只给 Python 包。浏览器本体要单独准备,详见安装。
漏了第二步的话,请求会直接失败并告诉你该跑哪条命令:
FAILED_PRECONDITION: 引擎 camoufox 的浏览器本体未就绪,请执行 python -m camoufox fetch
不会自动下载——那会让第一个请求卡着下 1 GB 然后超时,而下载还在后台继续跑。
[BROWSER]
enabled = true # 总开关。关掉后即使装了引擎也拒绝浏览器请求
engine = "auto"
browser = "chromium" # 只对 playwright / patchright 有意义
headless = true
executable_path = "" # 指向系统浏览器可省掉 150 MB 下载
args = []
no_sandbox = false # 容器里通常要打开
user_agent = "" # 留空则每次随机
viewport = { width = 1920, height = 1080 }
wait_until = "load" # load / domcontentloaded / networkidle / commit
block_resources = ["image", "media", "font"]
max_pages = 4
allow_scripts = false
# 只对 camoufox 生效
locale = ""
humanize = false
geoip = false
[BROWSER.proxy]
gateway = ""
bypass_list = []
[BROWSER.timeout]
page_load = 30
script_exec = 60| 值 | 什么时候算加载完 | 备注 |
|---|---|---|
commit |
收到响应头 | 最快,但 DOM 基本还是空的 |
domcontentloaded |
HTML 解析完 | JS 还没跑完 |
load |
所有资源加载完(默认) | 大多数场景够用 |
networkidle |
网络静默 500ms | 最慢,页面有轮询/长连接时可能一直等到超时 |
带 WebSocket、SSE 或定时轮询的页面不要用 networkidle——网络永远不会静默,
每个请求都会耗满 page_load 才返回。
只要 HTML 的话,拦掉图片、字体、媒体能省掉大量时间和带宽。默认已经拦了这三类。
可选值:image media font stylesheet script xhr fetch websocket other。
别拦
script和xhr——除非你确定不需要 JS 渲染的内容。拦了它们等于把 "用浏览器"这件事本身的意义拿掉了。
同时打开的页面上限。无头浏览器每个页面几十到上百 MB,不设上限很容易把机器打满。 超出的请求排队等待。
resp = d.get(
"https://example.com/product",
adapter="browser",
automation_script="return document.querySelector('#price').innerText",
)
print(resp.headers["x-ipclick-script-result"])automation_script 是在页面里执行的 JavaScript,不是 Python。 返回值经
x-ipclick-script-result 响应头带回。
几点要知道的:
-
默认关闭(
allow_scripts = false)。页面里的 JS 会自己发请求,[SECURITY]那套 URL 策略(禁云元数据、禁内网)对它完全不起作用—— 放开它等于把 SSRF 防线让开一整条。只在确认调用方全部可信时打开。 - 脚本写错(
SyntaxError/ReferenceError)被判为参数错误, 直接INVALID_ARGUMENT且不重试——语法错重试三次还是语法错。 - 脚本会自动规范化:可以写表达式、也可以写带
return的函数体,两种都认。 - 执行超时由
[BROWSER.timeout].script_exec控制(默认 60s)。 - 脚本文件较大时会被自动压缩传输,实测 8158 字节压到 350 字节,见 性能。
d.get(url, adapter="browser",
wait_for_selector="#content", # 等某个元素出现
wait_for_timeout=2000, # 额外固定等待(毫秒)
screenshot=True) # 返回截图wait_for_selector 比拉长 wait_for_timeout 好——前者一出现就继续,后者不管好坏都等满。
0.3.0 做了一轮专门的优化。修之前实测有单次点击耗时 296 秒的情况;修完之后:
| 路径 | 耗时 |
|---|---|
| 热路径(浏览器已起来) | 200~300 ms |
| 冷启动(首次拉起浏览器) | ~1.5 s |
| 永久性导航错误(域名不存在等) | 0.2 s(原先 16 s) |
关键改动:
-
浏览器实例复用,并在每次使用前用
is_connected()检查存活;死了就丢弃重建, 而不是拿着一个死连接反复超时。 - 超时预算随任务算:冷启动额外给 60s 宽限、常规开销给 15s, 不再用一个固定值同时套在冷热两条路径上。
-
永久性导航错误不重试:
ERR_NAME_NOT_RESOLVED、ERR_CONNECTION_REFUSED这类错误重试三次还是同样的结果。 -
browser先解析成具体引擎再做缓存键——否则adapter="browser"和adapter="camoufox"会各自起一个浏览器,内存翻倍。 - 调用方已经放弃就不再执行:gRPC 的 deadline 到了之后继续渲染纯属浪费, 结果没人接收。
细节见性能。
[BROWSER]
no_sandbox = true # 同时会带上 --disable-dev-shm-usage
executable_path = "/usr/bin/chromium"- 容器默认
/dev/shm只有 64 MB,Chromium 会崩——no_sandbox = true会自动加--disable-dev-shm-usage。 - 容器里通常没有 user namespace,需要
no_sandbox = true或--cap-add=SYS_ADMIN。 - 关沙箱意味着页面里的代码更容易影响宿主进程,所以默认是关的,要由部署方明确选择。
[BROWSER]
locale = "zh-CN" # 伪装的语言环境,留空则由 camoufox 生成
humanize = false # 模拟人类鼠标移动;true 用默认时长,也可写秒数如 1.5
geoip = false # 让时区/语言/经纬度与代理出口 IP 对上humanize 会显著拖慢每次请求,默认关闭。
geoip 只有 [BROWSER.proxy].gateway 能生效——按请求指定的代理是在 context 上设的,
那时指纹早就生成完了。
浏览器相关问题的定位路径见故障排查。快速自查:
ipclick config-info # 看"浏览器引擎"与"本体是否就绪"两行Web 管理端的总览页也会显示每个引擎的三态:未安装 / 包已装但本体未就绪 / 就绪。