С чего начать
Отменяйте тяжёлые запросы, которые не нужны задаче сбора данных, на каждой странице контекста браузера, а затем измерьте, действительно ли прогон передал меньше байтов.
Сначала измерьте задачу, потом блокируйте
Изображения, шрифты и видео могут составлять большую часть того, что загружает страница, а многие задачи сбора данных их вообще не читают. Блокировка таких запросов через перехват — распространённый способ сократить прокси-трафик. Поможет ли она вашей задаче, зависит от ваших страниц, поэтому начните с измерения: запустите задачу один раз как есть и запишите, куда уходят байты. scrapescope — бесплатный локальный измеритель с открытым исходным кодом: он показывает байты по хостам и по типам ресурсов для прогона 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 и запустите python block_resources.py после pip install playwright и playwright install chromium. Контекст также блокирует сервис-воркеры (service workers); причина объясняется ниже.
block_resources.py · Playwright для 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 и запустите node block-resources.mjs после npm install playwright и npx playwright install chromium.
block-resources.mjs · Playwright Chromium
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 · один маршрут для всех страниц
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, так что можно прерывать всё с этих хостов и не трогать типы ресурсов. Glob-шаблон вроде https://media.example.net/** соответствует любому URL на одном хосте; регулярное выражение может перечислить несколько. Хосты ниже — заполнители; настоящие имена берите из своего измерения, а не из универсального списка блокировки.
block_hosts.py · прерывание названных хостов
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-кеш
В справочнике API Playwright и для page.route(), и для browserContext.route() сказано: "Enabling routing disables http cache." (включение маршрутизации отключает HTTP-кеш). Как только зарегистрирован любой маршрут, таблицы стилей, скрипты и другие повторяющиеся ресурсы, которые браузер обычно взял бы из кеша, загружаются заново для каждой страницы. В задаче, которая загружает много страниц в одном контексте, эта дополнительная передача может свести на нет часть того, что убирает блокировка, или всё целиком. Поэтому теста на одной странице недостаточно: измеряйте весь прогон.
Блокируйте сервис-воркеры на время перехвата
Запросы, которые обрабатывает сервис-воркер страницы, могут обходить ваши маршруты: справочник API отмечает, что browserContext.route() не перехватывает запросы, перехваченные Service Worker, и рекомендует отключать сервис-воркеры при перехвате запросов, задав serviceWorkers значение 'block'. Это параметр контекста service_workers="block" (Python) и serviceWorkers: 'block' (Node.js) во всех примерах выше. Сайт, который зависит от сервис-воркера, например кеширует содержимое для работы офлайн, может вести себя иначе, когда не может его зарегистрировать.
Блокировка может изменить поведение страницы
Блокировка изображений, медиа и шрифтов может сломать извлечение данных, изменить поведение страницы или привлечь внимание антибот-систем. Страница может ждать изображение, прежде чем отрисовать данные, которые вы читаете, подгружать контент только после медиа-событий или считать необычным браузер, который никогда не загружает изображения. Проверьте, что извлекаемые значения не изменились после включения маршрута, и держите блокировку в рамках того, что разрешают условия использования сайта.
Измерьте ещё раз и сравните
Запустите одну и ту же задачу дважды через одного и того же провайдера, один раз без маршрута и один раз с ним, и сравните два прогона. Второй измеренный прогон — это доказательство; модель или оценка — это прогноз. scrapescope умеет сравнивать два отчёта бок о бок в своём просмотрщике, включая байты по хостам и по типам ресурсов, так что видно, что убрал маршрут и что вернул потерянный HTTP-кеш.
Перед выпуском
- Измерьте задачу один раз без блокировки, прежде чем что-либо менять.
- Регистрируйте маршрут на контексте браузера до загрузки первой страницы.
- Блокируйте сервис-воркеры в параметрах контекста, пока активны маршруты.
- Убедитесь, что извлекаемые значения не изменились после блокировки.
- Сравните второй измеренный прогон через того же провайдера, прежде чем полагаться на изменение.
Источники и дополнительное чтение
Технические материалы, использованные при подготовке руководства. Сверяйтесь с документацией вашей версии и с поддерживаемой конфигурацией вашего провайдера.