bigywater.tech
爬虫工程 · 57

浏览器复用:每次新开与复用上下文的成本

用浏览器抓取时,每个页面都启动一个新浏览器最干净也最慢。复用浏览器的几种方式在速度、状态隔离和内存上各有取舍。这篇在本机用同一个页面,对比每页启动浏览器、新标签页、独立 browser context 和同一个标签页,并看忘了关标签页会怎样。

452.1 对 5.7 ms
每页启动新浏览器与反复用同一个标签页,每页加载的中位耗时
29 / 29 对 0 / 29
默认 context 与独立 context 里,下一次加载带上之前页面 Cookie 的次数
69 个进程
60 次加载每次打开标签页但不关,结束时的进程数(关闭则是 9 个)

速度

实测 · 本机执行输出
  每页启动一个新浏览器                        总耗时  46862 ms;每页中位  452.1 ms
  复用浏览器,每页一个新标签页(默认 context)        总耗时   2289 ms;每页中位   38.0 ms
  复用浏览器,每页一个新的独立 context            总耗时   5232 ms;每页中位  114.1 ms
  复用浏览器,反复用同一个标签页                   总耗时   1305 ms;每页中位    5.7 ms

每页启动一个浏览器 452.1 ms/页;复用浏览器后新开标签页 38.0 ms/页,独立 context 114.1 ms/页(创建销毁 context 有开销),同一个标签页反复导航只要 5.7 ms/页。

状态隔离

实测 · 本机执行输出
  策略                                    第 2–30 次带了之前的 Cookie         图片请求数(缓存命中则少)
  每页启动一个新浏览器                                 0 / 29              30
  复用浏览器,每页一个新标签页(默认 context)                29 / 29               1
  复用浏览器,每页一个新的独立 context                     0 / 29              30
  复用浏览器,反复用同一个标签页                           29 / 29               1

默认 context 里的标签页共享 Cookie 和 HTTP 缓存:下一页带上了上一页的 Cookie(29/29),图片只请求 1 次。独立 browser context 和新的浏览器进程一样互相隔离:0/29 带 Cookie,图片请求 30 次。抓取需要隔离登录状态时,独立 context 比整个浏览器便宜。

内存

实测 · 本机执行输出
  复用浏览器,每页一个新标签页(默认 context)    进程数 10,RSS 之和 1181 MiB
  复用浏览器,每页一个新的独立 context        进程数 8,RSS 之和 1116 MiB
  复用浏览器,反复用同一个标签页               进程数 10,RSS 之和 1345 MiB

三种复用方式的内存都稳定在 1100–1350 MiB(进程树 RSS 之和,偏大)。同一个标签页反复导航略高一些。

别忘了关标签页

实测 · 本机执行输出
  每次打开新标签页但不关(60 次后):进程数 69,RSS 之和 7990 MiB
  每次打开新标签页并关闭(60 次后):进程数 9,RSS 之和 1214 MiB
  反复用同一个标签页(60 次后):进程数 13,RSS 之和 1582 MiB

每页打开新标签页但不关,60 次后有 69 个进程、7990 MiB。关闭要用 /json/close/<id> 或 Target.closeTarget;断开 websocket 连接并不会关闭标签页。

分步演示

浏览器复用
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / requests 2.34.2 / aiohttp 3.14.3 / lxml 6.1.1(系统 Python);所有请求只发给本机自己起的测试服务器(127.0.0.1,端口 9960 以上,不访问任何真实网站);服务器可以按实验需要制造延迟、断连、压缩、重定向等行为,并记录收到的原始请求;计时用 time.perf_counter,数字每次运行会有小幅抖动。Chrome 154 通过 pcdp.py 控制(Browser 类使用 Target.createBrowserContext / createTarget / disposeBrowserContext);页面是本机服务器上的一个小页面,设置 Cookie 并引用一张带 Cache-Control: max-age 的图片;每页等到 document.title 变成 ready 再计时;内存是 ps 汇总的进程树 RSS,重复计算共享页;一开始我的 Page.close() 只断开了 websocket 而没有关闭标签页,发现之后已改为真正关闭,第 54、55 篇也据此重跑并更新了数字
    • 没有验证:真实站点(大量 JS、广告、长连接)上复用的内存增长;长时间运行(几小时)的浏览器内存泄漏与定期重启的策略;持久化的 user-data-dir 里登录状态的复用;并发多个 context 同时加载的吞吐;Playwright/Puppeteer 的 context 抽象的额外开销;浏览器崩溃后的恢复
    京ICP备2026067502号-1京公网安备11010502063487号