跳到主要内容
浏览器自动化 · MOOPROXY FIELD NOTES

Playwright 如何减少代理流量:拦截资源与缓存的取舍

先定义任务需要什么,再决定不加载什么。文本提取、截图验收和登录流程,需要完全不同的资源策略。

节省流量,也要保住结果;按任务保留必要资源;HTML / 脚本 / 接口 / 所需样式;用对照实验做决定;正常加载 ↔ 选择性拦截:比较流量与完整性
示意图由 MooProxy 原创。主题插画:OpenMoji · CC BY-SA 4.0 (未修改)

先决定哪些资源可以省

任务 可以优先尝试 必须检查
提取页面文本和价格 不加载图片、视频 数据是否依赖懒加载或前端脚本
页面截图、视觉验收 保留图片、字体和样式 截图不能因省资源而失真
登录和多步骤流程 先保留完整页面 脚本、Cookie、会话和验证组件是否正常
已有正式数据 API 优先按 API 文档接入 权限、配额与返回字段是否满足需求

不要一上来拦截所有 JavaScript、CSS 或 XHR。页面可能看起来“加载快了”,但真正的数据接口根本没有运行。对受限页面应按站点权限和接入要求处理,而不是靠重试绕过访问限制。

一个容易忽略的事实:拦截会禁用 HTTP 缓存

Playwright 官方说明:启用 browser_context.route() 会禁用 HTTP 缓存。因此,“拦掉一些图片”不保证整个多页面任务更省流量。首次访问大量独立页面和重复访问同站页面,结果可能不同。[1]

另外,Service Worker 接管的请求可能不会被常规路由拦截。文档建议在需要拦截时将 service_workers 设为 block。[1][2] 这个设置也可能改变应用行为,应作为实验条件记录,不能悄悄改变后还把两组数据当作完全相同的业务流程。

最小示例:只拦截图片与媒体

依赖安装:python -m pip install playwright,然后 python -m playwright install chromium。本例使用控制台生成的 HTTP 代理入口,把不含认证的 http://HOST:PORT 设置为 PROXY_SERVER;认证单独传入,避免手工拼接 URL。

import getpass
import os
from collections import Counter
from playwright.sync_api import sync_playwright

BLOCK_MEDIA = True  # Compare with False in a separate run.
counts = Counter()
username = input("Proxy username: ").strip()
password = getpass.getpass("Proxy password: ")

with sync_playwright() as p:
    browser = p.chromium.launch(proxy={
        "server": os.environ["PROXY_SERVER"],
        "username": username,
        "password": password,
    })
    try:
        context = browser.new_context(service_workers="block")
        if BLOCK_MEDIA:
            def handle(route):
                kind = route.request.resource_type
                if kind in {"image", "media"}:
                    counts["blocked_" + kind] += 1
                    route.abort()
                else:
                    route.continue_()
            context.route("**/*", handle)
        page = context.new_page()
        page.on("request", lambda r: counts.update([r.resource_type]))
        page.goto("https://example.com/", wait_until="domcontentloaded",
                  timeout=30000)
        heading = page.get_by_role("heading", name="Example Domain")
        heading.wait_for(state="visible", timeout=10000)
        print("validated heading:", heading.inner_text())
        print("request counts (not bytes):", dict(counts))
        context.close()
    finally:
        browser.close()

把 BLOCK_MEDIA 改为 False 跑一组基线,再设为 True 跑一组实验。示例中的 example.com 仅用于演示等待与验证;测试真实任务时,必须替换为你有权访问的页面和真正需要的业务定位器,不能只检查标题。

request 事件包括被拦截的请求,上述计数是请求数量,不是流量字节数。示例也没有声称节约某个百分比。测试应结合独立账号的计量变化或适当的网络统计,固定相同工作量后比较。

等待业务完成,不要无限等“网络空闲”

页面可能持续轮询或保持连接,仅使用网络空闲作为完成条件会让任务等待很久。更可控的方法是先等待 DOM 就绪,再等待目标表格、价格元素或指定接口响应,设置清晰的超时。

domcontentloaded 本身也不证明数据完整。分页、滚动懒加载和异步价格仍需要额外等待与字段校验。如果图片被拦后影响滚动布局或触发逻辑,应恢复该类资源,或只对确认无关的域名、路径进行选择性处理。

用同一份清单验收两组结果

  1. 固定目标页面集合、地区、会话策略、并发及等待条件。
  2. 两组都使用清晰定义的缓存与 Service Worker 条件。
  3. 记录任务数、正确结果数、耗时、请求类型和实际流量。
  4. 核对关键字段、分页结果和失败原因,而非只比页面加载时间。
  5. 比较每条有效结果的用量;若缺数据或重跑增加,撤回对应拦截规则。

只有任务需要的结果完整且用量下降,优化才成立。把规则做成可开关的配置,保留一键回到正常加载的方式,比长期维护大量脆弱的 URL 黑名单更容易排障。

参考资料与编辑说明

资料核对:2026-10-19。本文为官方文档基础上的原创整理;代码为配置示例,不代表生产性能承诺。

  1. Playwright: BrowserContext.route
  2. Playwright: network interception and Service Workers

准备好验证你的连接了吗?
从控制台生成配置,按指南完成小样本测试。

打开控制台