Содержание урока · 10 разделов
- Зачем отключать картинки и шрифты
- Флаг Chrome: blink-settings на весь процесс
- Перехват Fetch: блокировка по URL и типу
- Ресурс → можно ли отключить → чем платим
- Что ломается: lazy-loading и скриншоты
- Измерение эффекта и связь с таймаутами
- Чек-лист блокировки ресурсов
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
Страница новости весит полтора мегабайта, а текста в ней — пятнадцать килобайт: всё остальное — картинки, шрифты, счётчики и стили, которые парсеру не нужны, но которые браузер честно качает, декодирует и держит в памяти. На сотнях страниц это часы времени и гигабайты трафика, а каждый лишний запрос — ещё и шанс словить перишаговый таймаут из пятнадцатого урока. Блокировка мусора — самый дешёвый способ ускорить обход: не оптимизировать код, а просто не качать то, что никогда не понадобится. В chromedp это делается на трёх уровнях, и у каждого своя цена. Флаг на процесс груб, но бесплатен: одна строка при старте. Перехват запросов гибок: правила по типу и адресу, но это ручной код. CSS-подмена — паллиатив, который убирает шрифты и фоны, но не трогает теги img. Выигрыш зависит от сайта: на лёгких текстовых страницах он скромный, на медиа-тяжёлых — кратный, поэтому ниже не только приёмы, но и способ измерить их на вашей цели, а не в чужих статьях. Ниже — все три уровня, таблица «ресурс против цены отключения», разбор поломок и измерение: как убедиться, что выигрыш реален, а не выдуман.
Зачем отключать картинки и шрифты#
Экономия трафика начинается с аудита: откройте вкладку Network на типичной странице цели и посмотрите, сколько весят типы image и font против document. Типовая раскладка медиа-тяжёлой страницы — до 80% байтов уходит на картинки и шрифты, и это байты, которые текстовому парсеру не достанутся никогда. Помимо трафика каждый ресурс стоит соединения: TLS-рукопожатие, очередь, декодирование, память под распакованный битмап; шрифты вдобавок задерживают отрисовку текста, пока не подгрузятся сами. Для извлечения данных через chromedp.Text всё это чистый убыток: страница платит за то, что не читается. Обратная сторона: картинки и шрифты жизненно нужны, когда вы снимаете скриншоты, верстаете PDF или проверяете, что страница выглядит как положено, — там отключение сломает задачу, и граница видна в таблице ниже. Правило простое: сначала тип задачи, потом уровень блокировки. Текстовому обходу — агрессивный флаг на весь инстанс, скриншотному сервису — точечный перехват или ничего, потому что серый скриншот без картинок никому не сдался.
Флаг imagesEnabled и замер времени загрузки
GOopts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.Flag("blink-settings", "imagesEnabled=false"),
)
allocCtx, allocCancel := chromedp.NewExecAllocator(context.Background(), opts...)
defer allocCancel()
ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()
start := time.Now()
var text string
err := chromedp.Run(ctx,
chromedp.Navigate(url),
chromedp.WaitVisible("article"),
chromedp.Text("article", &text),
)
log.Printf("готово за %s, %d символов, err=%v",
time.Since(start), len(text), err)Flag — универсальная опция аллокатора: строковое значение уезжает в командную строку Chrome как --blink-settings=imagesEnabled=false. Замер строится на time.Since вокруг Run: Navigate плюс WaitVisible дают полное время до готовности статьи. Текст приходит через chromedp.Text — картинки не участвовали ни в загрузке, ни в извлечении, и страница отдала контент быстрее.
Флаг Chrome: blink-settings на весь процесс#
chromedp блокировка изображений самым грубым способом — это флаг: chromedp.Flag("blink-settings", "imagesEnabled=false"). Из исходников библиотеки видно, что Flag принимает имя и значение любого типа: строка уезжает в командную строку как --name=value, булево — как голый --name; Blink — движок рендеринга, и эта настройка запрещает ему запрашивать картинки вовсе: вёрстка живёт, на месте картинок пусто, трафика ноль. Ключевое свойство — область действия: флаг ставится на аллокатор, то есть на весь процесс браузера, и все вкладки этого инстанса будут без картинок; выключить для одной вкладки нельзя. Отсюда дизайн: если в программе соседствуют текстовый парсер и скриншотчик, заводите два аллокатора с разными флагами — инстанс в chromedp дёшев, это отдельный процесс с собственными настройками. Флаги читаются один раз при старте процесса: поменять на лету нельзя, только пересоздать браузер — за гибкость на живом браузере отвечает перехват из следующего раздела.
Дежурный сервис снимал скриншоты витрин для отчётов, и кто-то «для скорости» добавил imagesEnabled=false в общий аллокатор — тот, из которого брали контексты все задачи подряд. Текстовые задания правда ускорились, а скриншоты превратились в белые листы с подписями: картинки не скачались, лейаут сжался, товарные карточки схлопнулись в полоски. Поиск занял день, потому что флаг сработал не там, где его читали: виноват оказался общий аллокатор, а не скриншотный код. Лечение — два аллокатора: блокирующий для текста и чистый для визуала, и правило «флаг на процесс, а не на программу». Спорное мнение с условием: один общий флаг допустим, если скриншотные задачи ходят в отдельный браузер по дизайну, а не по привычке — иначе рано или поздно кто-то их смешает.
Перехват Fetch: блокировка по URL и типу#
Флаг не умеет нюансов: он либо отключает картинки везде, либо нигде. Точность даёт перехват из семнадцатого урока: Fetch-домен останавливает браузер перед каждым запросом и даёт решить его судьбу. Включается перехват вызовом fetch.Enable с паттернами: RequestPattern описывает, какие запросы ставить на паузу, — поля URLPattern и ResourceType; паттерны на image и font ловят ровно мусор и не трогают документы и скрипты. Каждый пойманный запрос приходит событием EventRequestPaused, и ответов два: ContinueRequest пропустить дальше или FailRequest с причиной ErrorReasonBlockedByClient — браузер увидит обычный сетевой отказ, страница отработает его как недоступный ресурс. Здесь доступен и адрес: чёрный список трекеров, белый список для CDN картинок нужного домена, правило «блокировать всё тяжёлое с чужих хостов». Плата за гибкость — код: обработчик событий, горутина на каждое событие и аккуратность с контекстом.
Fetch-блокировщик картинок и шрифтов
GOimport (
"github.com/chromedp/cdproto/fetch"
"github.com/chromedp/cdproto/network"
)
// обработчик ставится до Navigate
chromedp.ListenTarget(ctx, func(ev interface{}) {
paused, ok := ev.(*fetch.EventRequestPaused)
if !ok {
return
}
go func() {
if err := fetch.FailRequest(paused.RequestID,
network.ErrorReasonBlockedByClient).Do(ctx); err != nil {
log.Printf("блок: %v", err)
}
}()
})
err := chromedp.Run(ctx,
fetch.Enable().WithPatterns([]*fetch.RequestPattern{
{URLPattern: "*", ResourceType: network.ResourceTypeImage},
{URLPattern: "*", ResourceType: network.ResourceTypeFont},
}),
chromedp.Navigate(url),
)С ListenTarget из шестнадцатого урока обработчик ловит паузы. Паттерны с ResourceType сужают перехват: на паузу встают только картинки и шрифты, остальное браузер не трогает вовсе. Каждому событию отвечает FailRequest с причиной BlockedByClient — страница видит сетевой отказ. Горутина на событие обязательна: обработчик не должен тормозить поток событий.
Ресурс → можно ли отключить → чем платим#
| Ресурс | Можно ли отключить | Чем платим |
|---|---|---|
| Картинки | да: флаг blink-settings или Fetch-паттерн | серые скриншоты, возможен сломанный lazy-loading |
| Шрифты | да: Fetch-паттерн или CSS-подмена | меняется ширина букв — ломаются координаты кликов |
| Фоновые CSS-картинки | да: CSS background-image: none | почти ничего: фон на извлечение текста не влияет |
| Стили | осторожно: только точечно | без CSS скрытые элементы не кликнутся, лейаут плывёт |
| Скрипты | обычно нет | SPA перестанет рендерить данные — уйдёт и контент |
| Аналитика и трекеры | да: Fetch по URL-чёрному списку | практически ничего: меньше шума и трафика |
Третий способ — самый мягкий: не запрещать загрузку, а убедить страницу не запрашивать лишнее. Шрифты отпадают сами, если переопределить font-family на системные: браузер качает веб-шрифт только когда элемент реально им оформлен, и подмена семейства одним style-тегом отменяет загрузку всех @font-face целиком. Картинки так не убрать: атрибут src в HTML загружается независимо от CSS, а display:none лишь прячет уже скачанное, — поэтому CSS-приём про шрифты, фоны и хвосты: background-image: none вырубает фоновые картинки, а вот тегу img он не указ. Внедряется подмена через Evaluate из десятого урока: одна строка кода создаёт style-тег с нужными правилами. Достоинство метода — точечность: применить можно к одной вкладке, не трогая соседние, и без перезапуска браузера; недостаток честный — это договорённость с вёрсткой, а не запрет на уровне сети, и страницы со своими стилями могут переиграть подмену специфичностью селекторов. Отдельный бонус: без веб-шрифтов почти исчезает сдвиг раскладки в момент загрузки, и WaitVisible на текстовые элементы срабатывает стабильнее.
Что ломается: lazy-loading и скриншоты#
Отключённые ресурсы меняют поведение страницы, и два эффекта надо знать заранее. Первый — lazy-loading: у сайтов с ленивыми картинками контент часто появляется по IntersectionObserver, когда картинка вот-вот войдёт в экран; с imagesEnabled=false картинки «не существуют», триггер молчит, и часть текста может не отрендериться вовсе. Для скрапинга это обычно не больно — основной контент лежит в HTML, — но проверка обязательна: сравните извлечённый текст с блокировкой и без на десятке страниц и зафиксируйте расхождения. Второй эффект — скриншоты из седьмого урока: снимок страницы без картинок — это серые заглушки, съехавший лейаут и места под изображения нулевой высоты; для визуальных задач блокировка запрещена полностью. Третий, более редкий: скрипты, которые ждут события onload у картинок или считают метрики по их размерам, — они виснут или делят на ноль. Общий принцип: блокировка меняет страницу, поэтому выборочные сверки текста — часть пайплайна, а не паранойя.
В песочнице этого сайта эффект не показать: она исполняет тематическую программу на чистом Go без браузера, и никакого сетевого трафика картинок там нет. Замер делается локально: возьмите десять-двадцать адресов цели, прогоните с флагом и без на одном железе и сравните медиану, а не лучший прогон — тёплая сеть и кэш DNS умеют врать. Заодно сложите байты: сколько трафика съели картинки в контрольном прогоне. Локальный запуск на медленном канале покажет выигрыш ярче всего — блокировка там ускоряет обход в разы, а не на проценты.
Измерение эффекта и связь с таймаутами#
Измерение — это time.Since вокруг Run, как в первом примере: одна и та же очередь страниц, два аллокатора — с флагом и без — и два числа в конце. Сравнивайте не среднее, а распределение: медиану и хвост p95 — блокировка выигрывает именно на хвосте, отрезая медленные медиа-запросы, которые тянут страницу за собой. Связь с пятнадцатым уроком прямая: таймауты подбираются под распределение времени страницы, и после блокировки хвост укорачивается — перишаговый потолок можно снизить без роста доли ретраев, а общий дедлайн задания ужать, пропустив больше страниц за окно расписания. Вторая метрика — байты: счётчик из перехвата или внешний замер на интерфейсе. Третья — доля страниц, где извлечённый текст совпал с контрольным прогоном без блокировки: это индикатор сломанного lazy-loading, и растущий процент здесь — сигнал вернуть ресурсы хотя бы выборочно, через Fetch-паттерн для нужного домена.
Чек-лист блокировки ресурсов#
- аудит Network перед блокировкой: сколько реально весят image и font на вашей цели
- текстовый обход — флаг imagesEnabled=false на весь инстанс: дёшево и грубо
- выборочная блокировка — Fetch-паттерны по ResourceType и URL из семнадцатого урока
- шрифты добиваются CSS-подменой font-family; тег img CSS не отменяет
- скриншоты и PDF — без блокировки: им отдельный аллокатор без флага
- lazy-loading проверяется сверкой текста с блокировкой и без
- медиана и p95 вместо среднего: блокировка выигрывает на хвосте распределения
Проверка: флаг blink-settings
Что сделает chromedp.Flag("blink-settings", "imagesEnabled=false") при старте аллокатора?
Проверка: раскладка по аллокаторам
В программе текстовый парсер и скриншотный сервис. Как разложить блокировку по аллокаторам?
Что попадёт под перехват
Прочитайте код и выберите, что он выведет:
fetch.Enable().WithPatterns([]*fetch.RequestPattern{
{URLPattern: "*", ResourceType: network.ResourceTypeImage},
{URLPattern: "*", ResourceType: network.ResourceTypeFont},
})
// обработчик шлёт FailRequest каждому пойманному запросу
// затем Run: Navigate(url) на страницу со скриптами и стилямиСоберите блокирующий аллокатор
Восстановите опции и создание аллокатора с флагом отключения картинок.
Расставьте каркас блокирующего обхода
Соберите по порядку: опции с флагом, аллокатор, контекст и первый замер.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: блокировка ресурсов
Что дальше: экономия — тактический приём, масштаб — стратегический, и они усиливают друг друга. Девятнадцатый урок соберёт пул браузеров: воркеры над каналом задач, лимиты вкладок и несколько инстансов Chrome — там блокированный флагом инстанс становится дешёвой единицей масштаба, ведь сэкономленные мегабайты умножаются на число воркеров. Перед пулом полезно перечитать и пятнадцатый урок: дедлайны шагов в параллели работают точно так же, только страниц в минуту больше. Практика — локально: нужны установленные Go и Chrome, песочница сайта программы без браузера не исполняет, поэтому честный замер трафика и времени делается только на настоящем интернете — та же оговорка, что с таймаутами. Домашняя проверка: прогоните двадцать страниц цели с флагом и без, сравните медиану и p95, посчитайте сэкономленные байты и найдите страницу, где lazy-loading без картинок недоложил текст, — опознать её вы теперь умеете.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- Медиа-ресурсы съедают до 80% трафика страницы, а текстовому парсеру не нужны: блокировка — самый дешёвый разгон обхода.
- chromedp.Flag — универсальная опция аллокатора: строка уезжает в Chrome как --name=value и действует на весь процесс.
- imagesEnabled=false отключает картинки во всех вкладках инстанса; для одной вкладки это сделать нельзя.
- Точность даёт Fetch-перехват: паттерны по ResourceType image и font, решение по запросу — ContinueRequest или FailRequest.
- CSS-подмена отменяет загрузку шрифтов и фонов, но не отменяет src у тега img.
- Lazy-loading при блокировке может недоложить текст: сверяйте содержимое с контрольным прогоном.
- Скриншотам и PDF картинки нужны: им — отдельный аллокатор без блокирующих флагов.
- Измеряйте медиану, p95 и байты: блокировка выигрывает на хвосте распределения времени страницы.
- Укороченный хвост позволяет ужать перишаговые таймауты без роста доли ретраев.
Домашнее задание
Соберите два аллокатора — с флагом imagesEnabled=false и без — и прогоните по двадцать страниц цели в каждом, замеряя time.Since для каждой страницы. Посчитайте медиану, p95 и суммарные байты. Затем добавьте на второй аллокатор Fetch-блокировку по паттернам image и font и сравните с флагом: скорость, гибкость, объём кода. Найдите страницу, где текст с блокировкой отличается от текста без, — разберите, lazy-loading это или что-то ещё. Финал: снизьте перишаговый таймаут после блокировки и убедитесь, что доля ретраев не выросла.
Личные заметкиПусто
Вопросы и ответы по уроку
Флаг отключил картинки, а страница всё равно медленная. Что ещё качается?
Посмотрите в перехвате, что осталось: обычно это шрифты, тяжёлые стили, аналитика и видео. Шрифты добиваются CSS-подменой или Fetch-паттерном на font, трекеры — URL-чёрным списком, видео — паттерном на media. Замер по категориям из вкладки Network покажет следующего кандидата: гасите типы по одному и следите за медианой, чтобы поймать вклад каждого в общее время загрузки.
Можно ли включать и выключать блокировку на живом браузере?
Флаги читаются при старте процесса и на лету не меняются — только новый инстанс с другим набором опций. На живом браузере управлять можно перехватом: fetch.Enable и Disable включаются на вкладке, а URL-правила обработчика читаются из конфига на каждое событие — их можно менять без перезапуска. Поэтому системы с разными режимами закладывают либо два аллокатора, либо перехват с конфигом.
Блокирую картинки, но WaitVisible на карточку не срабатывает. Почему?
Скорее всего, карточка появляется по скрипту, который ждёт загрузки или размеров картинки, а с imagesEnabled=false картинка не запрашивается и триггер молчит. Сверьте текст контрольным прогоном без блокировки: если контент там есть, ищите селектор на элемент, не зависящий от картинки, или ждите через Poll по тексту. В крайнем случае верните картинки выборочно — Fetch-паттерном только для домена с нужными изображениями.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
Testcontainers в Go: интеграционные тесты с Docker
Курс testcontainers-go: Postgres, Redis, Kafka и Mongo в тестах через Docker, wait-стратегии, сети, параллельность, CI и итоговый стенд. 25 уроков.
- Поднимать реальные зависимости в Docker из кода теста
- Использовать модули Postgres, Redis, MongoDB, Kafka и RabbitMQ
- Ждать готовность сервиса wait-стратегиями вместо sleep
- Собирать стенды из нескольких контейнеров и своего образа