# 在 Playwright 中屏蔽图片、字体和媒体以减少代理流量

Source: https://ipvolt.com/zh/guides/playwright-block-images-save-bandwidth
Markdown: https://ipvolt.com/zh/guides/playwright-block-images-save-bandwidth.md
Language: zh-CN

[ipvolt 首页](https://ipvolt.com/zh.md) / [指南](https://ipvolt.com/zh/guides.md) / 在 Playwright 中屏蔽图片、字体和媒体以减少代理流量

集成
发布于: 2026-10-09
阅读约 7 分钟
作者： ipvolt

在 Python 和 Node.js 中用 Playwright 的 route() 屏蔽图片、字体和媒体请求，可作用于整个浏览器上下文或按主机屏蔽，并说明 HTTP 缓存与 Service Worker 的注意事项。

## 起点

在浏览器上下文的每个页面上中止抓取任务不需要的大体积请求，然后测量这次运行是否真的少传输了字节。

## 先测量任务，再屏蔽任何内容

图片、字体和视频可能占页面下载量的很大一部分，而许多抓取任务根本不读取它们。通过请求拦截屏蔽它们，是减少代理流量的常见做法。它对你的任务是否有帮助取决于你的页面，所以先做一次测量：按原样运行一次任务，记录字节都去了哪里。[scrapescope](/zh/guides/scraper-bandwidth-report) 是一款免费、开源的本地流量计，可按主机和资源类型报告一次 Playwright 运行的字节数，并模拟屏蔽图片、媒体和字体的效果。

下面的示例使用 Playwright 1.63 和占位地址 `https://example.com/`。请把 TARGET_URL 指向你获准自动化访问的页面，并把代理设置和凭据放在你自己的启动选项或上下文选项里；仅仅试用屏蔽功能并不需要它们。

## 在 Python 中按资源类型屏蔽

Playwright 会给每个请求标上渲染引擎识别出的资源类型：document、stylesheet、image、media、font、script、texttrack、xhr、fetch、eventsource、websocket、manifest 或 other。在浏览器上下文上为所有 URL 注册一条路由，中止这三种大体积类型；其他请求照常继续。

保存为 block_resources.py，在执行 pip install playwright 和 playwright install chromium 之后运行 python block_resources.py。该上下文还屏蔽了 Service Worker，原因见下文。

### block_resources.py · Playwright for Python

```python
import os

from playwright.sync_api import Route, sync_playwright

TARGET_URL = os.environ.get("TARGET_URL", "https://example.com/")
BLOCKED_TYPES = {"image", "media", "font"}


def block_heavy(route: Route) -> None:
    if route.request.resource_type in BLOCKED_TYPES:
        route.abort()
    else:
        route.continue_()


with sync_playwright() as p:
    browser = p.chromium.launch()
    try:
        context = browser.new_context(service_workers="block")
        context.route("**/*", block_heavy)
        page = context.new_page()
        response = page.goto(TARGET_URL, wait_until="load")
        print({"status": response.status if response else None})
        context.close()
    finally:
        browser.close()
```

## 在 Node.js 中按资源类型屏蔽

Node.js 版本是同一条路由、同样的三种类型。route.request().resourceType() 返回类型，route.abort() 让请求在离开浏览器之前失败，route.continue() 则原样发送其余所有请求。

保存为 block-resources.mjs，在执行 npm install playwright 和 npx playwright install chromium 之后运行 node block-resources.mjs。

### block-resources.mjs · Playwright Chromium

```javascript
import { chromium } from 'playwright';

const TARGET_URL = process.env.TARGET_URL ?? 'https://example.com/';
const BLOCKED_TYPES = new Set(['image', 'media', 'font']);

const browser = await chromium.launch();
try {
  const context = await browser.newContext({ serviceWorkers: 'block' });
  await context.route('**/*', route =>
    BLOCKED_TYPES.has(route.request().resourceType()) ? route.abort() : route.continue());
  const page = await context.newPage();
  const response = await page.goto(TARGET_URL, { waitUntil: 'load' });
  console.log({ status: response?.status() });
  await context.close();
} finally {
  await browser.close();
}
```

## 应用到浏览器上下文中的每个页面

浏览器上下文上的路由对该上下文中任何页面发出的请求都生效：之后打开的页面、第二个标签页，以及页面通过 window.open() 打开的弹出窗口。单个页面上的路由只对该页面生效，因此弹出窗口或新标签页会重新加载自己的图片和字体。请在第一个页面加载之前注册上下文路由，并把它放进一个辅助函数，让每个任务都使用同一条规则。

当一个请求同时匹配两者时，页面路由优先于浏览器上下文路由；当多条页面路由都匹配时，最近注册的那条生效。每个上下文只保留一条屏蔽路由，以免出现意外。

### context-wide.mjs · 一条路由覆盖所有页面

```javascript
import { chromium } from 'playwright';

const TARGET_URL = process.env.TARGET_URL ?? 'https://example.com/';
const BLOCKED_TYPES = new Set(['image', 'media', 'font']);

// One route on the context covers every page the context opens, including
// tabs opened later and popups. Register it before the first page loads.
async function blockHeavyResources(context) {
  await context.route('**/*', route =>
    BLOCKED_TYPES.has(route.request().resourceType()) ? route.abort() : route.continue());
}

const browser = await chromium.launch();
try {
  const context = await browser.newContext({ serviceWorkers: 'block' });
  await blockHeavyResources(context);
  const first = await context.newPage();
  const second = await context.newPage();
  await Promise.all([
    first.goto(TARGET_URL, { waitUntil: 'load' }),
    second.goto(TARGET_URL, { waitUntil: 'load' }),
  ]);
  console.log({ pages: context.pages().length });
  await context.close();
} finally {
  await browser.close();
}
```

## 按 URL 模式屏蔽特定的大流量主机

有时问题出在某一个主机上：视频 CDN、广告服务器或字体服务。route() 也接受 URL 模式，因此你可以中止来自这些主机的所有请求，而不去动资源类型。像 `https://media.example.net/**` 这样的 glob 匹配一个主机上的所有 URL；正则表达式则可以列出多个主机。下面的主机只是占位符；真实的主机名应来自你的测量结果，而不是通用的屏蔽列表。

### block_hosts.py · 中止指定主机

```python
import os
import re

from playwright.sync_api import sync_playwright

TARGET_URL = os.environ.get("TARGET_URL", "https://example.com/")
# A glob covers every URL on one host; a regular expression can cover several.
HEAVY_HOST = "https://media.example.net/**"
HEAVY_HOSTS = re.compile(r"^https://(video\.example\.org|fonts\.example\.com)/")

with sync_playwright() as p:
    browser = p.chromium.launch()
    try:
        context = browser.new_context(service_workers="block")
        context.route(HEAVY_HOST, lambda route: route.abort())
        context.route(HEAVY_HOSTS, lambda route: route.abort())
        page = context.new_page()
        response = page.goto(TARGET_URL, wait_until="load")
        print({"status": response.status if response else None})
        context.close()
    finally:
        browser.close()
```

## 启用路由会关闭 HTTP 缓存

Playwright 的 API 参考对 page.route() 和 browserContext.route() 都写明："Enabling routing disables http cache."（启用路由会禁用 HTTP 缓存。）一旦注册了任何路由，样式表、脚本以及其他本来会被浏览器从缓存中复用的重复资源，就会在每个页面重新下载。对于在同一个上下文中加载许多页面的任务，这部分额外传输可能抵消屏蔽所省去的一部分流量，甚至全部。这就是只测一个页面不够的原因：要测量整次运行。

## 拦截请求时屏蔽 Service Worker

由页面的 Service Worker 处理的请求可能绕过你的路由：API 参考指出，browserContext.route() 不会拦截已被 Service Worker 拦截的请求，并建议在使用请求拦截时把 serviceWorkers 设为 'block' 来禁用 Service Worker。这就是上面每个示例中的上下文选项 service_workers="block"（Python）和 serviceWorkers: 'block'（Node.js）。依赖 Service Worker 的网站（例如用它缓存内容以便离线使用）在无法注册时，行为可能会有所不同。

## 屏蔽可能改变页面的行为

屏蔽图片、媒体和字体可能破坏数据提取、改变页面的行为，或引来反爬虫系统的关注。页面可能要等某张图片加载后才渲染你要读取的数据，可能只在媒体事件之后才懒加载内容，也可能把从不下载图片的浏览器视为异常。启用路由后，请确认提取到的值没有变化，并让屏蔽行为保持在网站条款允许的范围内。

## 再次测量并比较

通过同一家服务商把同一个任务运行两次，一次不带路由，一次带路由，然后比较这两次运行。第二次实测运行才是证据；模型或估算只是预测。[scrapescope](/zh/guides/scraper-bandwidth-report) 可以在其查看器中并排比较两份报告，包括按主机和按资源类型统计的字节数，让你看清路由去掉了什么，以及失去 HTTP 缓存又加回了什么。

## 上线之前

- 在做任何改动之前，先在不屏蔽的情况下测量一次任务。
- 在第一个页面加载之前，把路由注册到浏览器上下文上。
- 路由生效期间，在上下文选项中屏蔽 Service Worker。
- 确认屏蔽之后提取到的值没有变化。
- 在依赖这项改动之前，通过同一家服务商再做一次实测运行并比较。

## 参考来源与延伸阅读

- [Playwright: browserContext.route() (routing notes, v1.63)](https://playwright.dev/docs/api/class-browsercontext#browser-context-route)
- [Playwright: page.route()](https://playwright.dev/docs/api/class-page#page-route)
- [Playwright: request.resourceType()](https://playwright.dev/docs/api/class-request#request-resource-type)
- [Playwright: the serviceWorkers context option](https://playwright.dev/docs/api/class-browser#browser-new-context-option-service-workers)
- [Playwright for Python: browser_context.route()](https://playwright.dev/python/docs/api/class-browsercontext#browser-context-route)
- [Playwright issue 1090: routes and service workers](https://github.com/microsoft/playwright/issues/1090)

## 相关指南

- [在 Playwright 中配置代理](https://ipvolt.com/zh/guides/playwright-proxy-setup.md)
- [轮换代理与粘性代理：为会话连续性做好规划](https://ipvolt.com/zh/guides/rotating-vs-sticky-proxies.md)
- [修复 ERR_TUNNEL_CONNECTION_FAILED 错误](https://ipvolt.com/zh/guides/fix-err-tunnel-connection-failed.md)

## 关于 ipvolt

示例使用通用的代理设置，并附上原始技术文档链接。具体产品的行为请向你的服务商确认。ipvolt 仍在开发中。

[阅读英文原文](https://ipvolt.com/guides/playwright-block-images-save-bandwidth.md)

## 了解何时开放体验。

ipvolt · 开发中

我们正在为开发者和数据团队打造代理基础设施。加入意向名单，ipvolt 就绪时第一时间收到通知。

开放体验时仅发一封通知邮件，不发送其他邮件。

[申请抢先体验](https://ipvolt.com/zh/guides/playwright-block-images-save-bandwidth#waitlist-closing)

[隐私政策](https://ipvolt.com/privacy)

